Пиксельный поиск пути

29.09.2008, автор Stormit, рубрики: ActionScript, Flash игры, Игровые баннеры

Это продолжение предыдущего поста о простом движке для flash-игр. Тогда, в самом конце возникла мысль уйти от метода hitTest() и перейти на пиксельный просчёт. И чтобы сравнить оба способа и выбрать лучший вариант, сделал небольшой визуальный тест.

Кстати, в тот раз я написал не совсем правильный код. Команда break не выводила процесс из вложенного цикла и бессмысленно терялось много процессорных ресурсов. Странно, но никто по этому поводу не отписался. Значит либо все “внимательно” читают и пробуют, либо верят на слово ;)
Этот баг я поправил. Дальше по слайдам:

  1. Настало время сделать это. hitTest() или getPixel()? Чья возьмёт? Кто будет править игровым миром?
  2. Символ path немного изменился, теперь он чёрно-белый, где белый цвет отвечает за проходимость, а чёрный задает препятствия (отверстий теперь нет - закрашено всё).
  3. Код из прошлого примера заменяем на этот:
    import flash.display.BitmapData;
     
    step = 2;
     
    //переводим зону проходимости в пиксельный вид
    var pathBD = new BitmapData(path._width, path._height, false, 0x000000);
    pathBD.draw(path);
     
    path._visible = false;
     
    onEnterFrame = function () {
    	var dx = _xmouse - man._x;
    	var dy = _ymouse - man._y;
    	var angle = Math.atan2(dy, dx);
    	var dist = Math.sqrt(dx * dx + dy * dy);
    	if (dist > step) {
    		tgtX = man._x + step * Math.cos(angle);
    		tgtY = man._y + step * Math.sin(angle);
     
    		//Если цвет пиксела черный, значит препятствие
    		if (pathBD.getPixel(tgtX, tgtY) == 0) {
    			var dAngle = dAngleRadian(direction, angle);
    			workAngle = angle + dAngle * .8;
    			var outFlag = false;
    			for (var i = 0; i < 360 && !outFlag; i += 15) {
    				for (var j = -1; j <= 1; j += 2) {
    					var a = workAngle + radian(i) * j;
    					var tempX = man._x + step * Math.cos(a);
    					var tempY = man._y + step * Math.sin(a);
    					if (pathBD.getPixel(tempX, tempY) != 0) {
    						tgtX = tempX;
    						tgtY = tempY;
    						break;
    					}
    				}
    			}
    		}
    		var timeDx = tgtX - man._x;
    		var timeDy = tgtY - man._y;
    		direction = Math.atan2(timeDy, timeDx);
    		var dAngle = dAngleDegree(direction * 180 / Math.PI, man._rotation);
    		man._rotation += dAngle * .2;
    		man._x = tgtX;
    		man._y = tgtY;
    		play();
    	} else {
    		gotoAndStop(5);
    	}
    };
     
    function dAngleRadian (a1, a2) {
    	var da = a1 - a2;
    	if (da > Math.PI) {
    		da = -Math.PI * 2 + da;
    	} else if (da < -Math.PI) {
    		da = Math.PI * 2 + da;
    	}
    	return da;
    };
    function dAngleDegree(a1, a2) {
    	var da = a1 - a2;
    	if (da > 180) {
    		da = -360 + da;
    	} else if (da < -180) {
    		da = 360 + da;
    	}
    	return da;
    };
    function degree(a) {
    	return a / Math.PI * 180;
    };
    function radian(a) {
    	return a / 180 * Math.PI;
    };

    Чтобы меньше грузить процессор, я перевел анимацию персонажа в растр.

  4. А теперь небольшое сравнение. 50 юнитов, метод getPixel(). Для чистоты эксперимента поместите мышку в центр озера, чтобы объекты выполняли максимальный просчёт.
  5. 50 юнитов, метод hitTest()
  6. Зона без препятствий. На каждого юнита приходится один вызов функции каждый кадр. 150 юнитов, метод getPixel()
  7. 150 юнитов, метод hitTest()
  8. Специально для быстрых машин - 300 спартанцев и getPixel()
  9. 300 спартанцев и hitTest()

Лично у меня пиксельный способ работает быстрее, но… незначительно. На моем старичке Атлоне 2.5 всего лишь 3 фпс разницы. Честно говоря я ожидал большего, но если уж выбирать, то лучше пусть это будет BitmapData и метод getPixel(). Если же у вас по какой-то причине постоянно меняется рельеф местности, тогда наверное hitTest() будет более кстати.

Если кто-нибудь сделает толковую игру на таком движке, дайте ссылку :)

Интересно на 59%

(58) Хитрых на тему «Пиксельный поиск пути»

  1. Anton

    У меня 32 fps на обоих тестах - вообще никакой разницы.

  2. Stormit

    Разница незначительная.
    Просто у меня 13 и 16 фпс получается. А вам значит нужно наштамповать их штук 300 в зоне с препятствиями ;)

  3. Destroyer

    У меня тоже 32 и 32 :\ Никакой разницы.

  4. Stormit

    Добавил утяжелённый вариант - 300 юнитов (слайды 8 и 9)

  5. Anton

    Если навести мышку в центр правого озера, то при 300 юнитах оба варианта показывают 8 fps. Разницы никакой.

  6. Diomas

    У меня разница чувствуется только на последнем (300 с препятствиями) тесте 7 fps hitTest() vs 9 fps getPixel()

  7. Griman

    разницы нет. Думается наиболее ресурсоемкие здесь конструкции вроде Math.cos, Math.sqrt. Да и в общем, конечно, вычислять направление на каждом кадре для всех юнитов никаких ресурсов не хватит :)

  8. Destroyer

    Да, всеравно разницы никакой. и там, и там 19 fps.

  9. Stormit

    to Griman
    Так и есть. Сами по себе эти расчёты прилично тормозят.
    Частично от них можно избавиться если проверять расстояние без извлечения корня (расстояние и шаг в квадрате): if (dist2 > step2) {}
    А точки пробивать так: один раз создаётся вектор под 45 градусов назад от базового направления и дальше прибавлять его. Будет небольшое торможение о препятствие, что в общем-то естественно.

  10. Romano

    Эмс… интересно… а если сделать тот же поиск пути? Больше будет есть? Если его применять то можно избавиться от некоторой зацикленности во “впадинах” препятствий… Да и не будет постоянно пересчитываться когда будет движение объектов, только при смене позиции мышки…

  11. GB

    На последнем хитТест проигрывает, но не сразу постепенно “выдыхается”

  12. artem

    никакой раздницы товарищи!

  13. KLyK

    А где предыдущий пример? А то мне этот код не идет!
    Вообще getPixel не работает.

  14. Stormit

    Подозреваю что нужно экспортить под более позднюю версию флэш-плеера ;)

  15. KLyK

    Подозреваю что нужно экспортить под более позднюю версию флэш-плеера…
    Код?

  16. Stormit

    Мне кажется мы говорим на разных языках.
    Если у вас получался раньше предыдущий пример (он никуда с блога не исчезал), то просто замените его код на этот (приведён выше).
    В настройках Publish Settings (Ctrl+Shift+F12) выберите версию Flash Player 8 или выше.

  17. KLyK

    У меня кажись шестая версия Макромедии.
    А дело в том, что я не знаю ссылки на предыдущий пример.
    И спасибо за ответ

  18. Костя

    Получается символ патч и символ мэн должны быть на одном слое? А как же тогда цветная графика, куда её?

  19. Stormit

    Они должны быть на одной временной линейке (чтобы переменные в коде находили символы). Цветная графика вставляется сверху символа path, слоем выше.

  20. Костя

    Тогда мувик man не будет видно.

  21. Stormit

    Почему? Он лежит над цветной графикой.
    Каждому свой слой - как видите во флэше, так и в флэшке будет.

  22. Костя

    Перечитал код, у вас visible+false, врубился. Простите за излишние вопросы.

  23. Badim

    великолепный туториал. ну хочешь его на английский перевести и на мочимедия опубликовать? заодно посещаемость подымишь.

    еще раз спасибо, игру уже делаю =))))))) точнее есть, щас только туда это вставлю. покажу потом =)

  24. Stormit

    Можно попробовать, хотя мой английский оставляет желать лучшего :)

  25. Badim

    Английский исправим, это не проблема =)

  26. Badim

    что такое direction ? =)
    код не полный =))) ща его вроде вставил.. но чето не то =(

  27. Badim

    а все разобрался. это текущее направление движения в радианах +)

  28. Badim

    http://badim.ru/ef_dota/ <- герой и войска, которыми можно управлять обходят здания и башни по этому алгоритму =) мелкие косяки конечно есть.

  29. SmivaL

    при больших нагрузках видно оч хорошо (8, 9) так что getPixel() рулит

  30. Badim

    да, и гет пикселя есть важное приемущество =)
    битмапдатами проще управлять =)

  31. MayorAndrew

    Огромное спасибо за поиск пути! Супер сайт… Однако мне больше подошел с методом hit-test.
    Кстати, вот игра, куда включен поиск пути… Тоже стратегия =) http://fsstar.do.am/load/1-1-0-17

  32. MayorAndrew

    Извиняюсь… Эта ссылка лучше: (Не хочу принуждать желающих посмотреть игру регистрироваться на сайте) http://fsstar.do.am/_ld/0/17_city-sim0.swf
    Кстати, о игре Badim’а… Прикольная, только юнитов не видно иногда (чужих)… А так очень даже прелестно… (Особенно дизайн главного меню).
    Еще… Никто не подскажет, как лучше нарисовать идущего человека с видом сверху?

  33. Stormit

    Сейчас человек начинает обходить ров по часовой стрелке. Если мост поставить чуть ниже, он на него никак не отреагирует (а должен) и пойдет обходить пока его не нащупает.

  34. GB

    :)) это Settlers это Супер :о))

  35. GB

    MayorAndrew
    правда есть баги
    Можно заказать человечка и он будет стоять у ангара до упора, когда построил ферму человечки больше не заказывались.
    Ну и рейд охотников вокруг озера уже описали.

  36. MayorAndrew

    Да уж… Багов действительно достаточно.
    Однако, у меня была игра (я сам тоже сделал). Она тоже есть на сайте (CupKiller называется). Так вот, там иногда не удаляются патроны, которые вылетают снизу… А если игру запускать с компьютера все вроде работает.
    Об этих багах мне не раз говорили, однако ничего сделать с ними у меня пока не получалось… (Когда запускаю на компьютере, их нет…)
    Кстати, охотники по мосту потом все-таки проходят? Если да… то впринципе все понятно… (Поэтому строю два моста =))
    А можно-ли как нибудь это с помощью ГетПикселя реализовать? У меня идет ХитТест по невидимым путям, которые ставятся вместе с зданиями…

  37. Stormit

    Можно одновременно с постройкой сооружения (мост, здание…) в объекте BitmapData закрашивать соответствующую область чёрным (проще, наверное, хранить набор таких клипов и добавлять их в виде растра в карту пути).

  38. Badim

    http://badim.ru/ef_dota/
    у меня при постройке башен, зданий обновляеться карта проходимости.

  39. shaman4d

    К сожалению действительно разницы нету - а так хотелось. :(

  40. Алик

    Народ а как сделать с помощью с помощью хитеста, оно что-то совсем не работает со сложными фигурами.

  41. Stormit

    Не совсем понял вопрос.
    Статья как раз о том, как сделать это с помощью хиттеста или getPixel. Для сложных форм нужно просто подбирать параметры (описаны в статье), чтобы персонажи не дёргались и путь корректно находили.

  42. Алик

    В данном случае первый объект это точка с координатами х,у, а второй это вот всякие сложные фигурки. А если надо сложная фигура + сложная фигура, никак немогу разобраться как сделать :(

  43. Stormit

    Вот с этих “ложная фигура + сложная фигура” нужно сделать объект BitmapData. Он является “картой проходимости” в чёрно/белом виде. По нему и проверяется пересечение.
    Если у вас периодически добавляются и пропадают препятствия на карте, за этим нужно следить и закрашивать соответствующие зоны на “карте проходимости” чтобы они учитывались при поиске пути.

  44. smival

    Переписал на ас3, при входе в цикл обработки препятствия координаты нового шага сбрасываются в NaN и соответственно мэн прыгает в 0,0
    что бы это могло быть?

  45. smival

    ага, вот и причина - не объявленные переменные:

    tgtX
    tgtY
    direction
    workAngle

  46. Stormit

    AS3 более строг в написании. Поэтому я для простоты и доступности пишу здесь на AS1

  47. Сергей

    Можете дать исходник этой флешки zloudoktor(собака)mail.ru
    Или на вашем сайте

  48. Lgunchik

    А у мя шото эт код не работает что я делаю не так?

  49. Lgunchik

    var pathBD = new BitmapData(path._width, path._height, false, 0×000000); вот в этой строчке ошибка, а еслив её удалить то объект не реагирует на столкновение

  50. vasya

    при помощи drive() можно и тут менять рельеф местности

  51. kabanov

    Сайт супер! Но посты бывают редко (
    Пытаюсь переписать на AS3. Все переменные объявил, ошибок нет, но man все время сбрасывается в точку (0,0). Как это можно исправить?

  52. DARKO

    Привет, можете мне какимто образом помоч…у меня такая дилема, ни один из движков непроходимих обектов произвольной формы почемуто неработает, уже 4 дней все пробою но ничего неработает,…что ето может быть ?на каком языке ети скрипты? ктота может дать ‘исходник’?

  53. serg

    А размеры человека кто будет учитывать?
    Сайт супер! Спасибо.

  54. orbit

    Поиск пути сделан очень не оптимально, сойдет только если очень мало юнитов.

    Я делал поиск пути методом потенциальных полей:
    http://www.swfcabin.com/open/1272019894

    F8 +10 человек
    F9 +50 человек

    У меня на asus 1.6 тянет 400 юнитов с фпс около 30, у некоторых за тыщу тянет…
    При этом код еще можно оптимизировать, чтоб был быстрее.

    Основная идея - зачем определять коллизии с точностью до пикселя? Можно же поделить все на сетку 8х8…

  55. Xcite

    На АS3 не работает, при столкновении бросает героя в (0,0).

  56. Богдан

    Разница есть! у меня на 50 юнитах при getPixel fps=20, а при hitTest fps=16

  57. sm2

    А подскажите как сделать 50 юнитов как в этом примере? мне очень срочно надо!
    Хочу сделать несколько противников движущихся на игрока. Противника сделал,
    а вот как сделать несколько ни как не получается. Подскажите кто-нибудь!

  58. Straga

    Еще для обхода препятствий есть волновой метод. В общем тоже неплохой метод, правда малость придется помучатся еще чтобы если бежит толпа, чтобы они не накладывались друг на друга. В стратегиях его вроде чаще всего и используют.

Оставить комментарий