Вход в систему
Последние статьи
Самые комментируемые статьи
Один из важнейших кирпичиков любого игрового движка — модуль поиска путей. Он нужен в играх самых разнообразных жанров. В action pathfinding (именно этот устоявшийся термин используется в документации к разного рода движкам) поможет монстру максимально быстро найти героя или убежать от него, не запутавшись в четырех стенах. В стратегиях отряд лучников, которого неумолимая длань игрока послала на противоположный конец карты атаковать
Рождение матрицы Сначала слегка вас напугаю. Задачи, подобные этой, рассматриваются в дискретной математике и описываются теорией графов. Сама теория сопровождается водопадом заковыристых математических значков и такими страшными словосочетаниями, как “отношение контрпозиции”, “эйлеров цикл” и даже “матрица весов”. Но не придумали еще такой математической теории, которую нельзя было бы перевести на нормальный человеческий язык. Поэтому эта часть “Игрового конструктора” будет не сложнее остальных, а в чем-то даже и легче. Ведь мы, наконец, переходим к легко осязаемой практике. Прежде чем разбирать сами алгоритмы, сделаем важное замечание. Все алгоритмы поиска путей работают или с массивом, который отождествляет игровой мир, или со списком опорных точек — так называемых waypoints. Первый способ подходит для движков, у которых все объекты стоят строго в узлах воображаемой сетки и имеют одинаковые или кратные размеры. К ним относятся почти все тайтловые движки, движки разных классических игр и головоломок и даже движки стратегий с простой геометрией объектов. В них во всех карту игры можно представить в виде массива: Map:array[1..M,1..N] of integer; где MхN — размер карты в ячейках, а в каждой ячейке содержится цифра, соответствующая типу территории. Например: 1 — стена, пройти невозможно, 2 — ограда, пройти невозможно, но можно стрелять сквозь нее, 3 — забор, можно сломать и пройти, но на это затратится 5 элементарных единиц времени, 4 —
Для стратегий с объектами сложной формы и всех нормальных экшенов карта представляется в виде совокупности waypoints. Каждую опорную точку можно представить в виде: PWaypoint=^TWaypoint; TWaypoint=record Position:TGLCoordinates; Connections:array of PWaypoint; Territory:integer; Radius:double; end; Первая строчка объявляет тип указателя на TWaypoint. В самой записи TWaypoint поле Position — координаты опорной точки, Connections — динамический массив с указателями на ближайшие опорные точки, в которые можно без препятствий дойти из этой точки, Territory — тип территории, на которой стоит опорная точка. Впрочем, этот параметр необязателен. Вы можете симулировать его, например, высотой того места, на котором стоит опорная точка. Последний необязательный параметр Radius показывает зону действия опорной точки, то есть на каком максимальном расстоянии может находиться юнит, чтобы считать себя стоящим на ней. Все опорные точки для одной карты лучше хранить в списке или динамическом массиве. Как создавать эти опорные точки — вопрос особый. Вы можете
На гребне волны... Один из самых простых для понимания алгоритмов поиска путей и вместе с тем довольно эффективный — волновой поиск. Он идеально подходит для небольших карт, которые можно представить в виде двумерного массива ячеек. Для начала вам нужно завести еще один двумерный массив целых чисел такого же размера, как и основной массив карты. Алгоритм работает следующим образом. Находим точку А, из которой начинается поиск, и в этом месте в
Алгоритм неплохо справляется с разного рода тупиками и всегда найдет путь из A в B, если он вообще существует. Другое дело, что этот путь редко будет кратчайшим из возможных. К сожалению, волновой поиск нельзя использовать на больших картах (с десятком тысяч и более клеток), так как он работает очень медленно. Поиск в глубину Предыдущий алгоритм иногда называют поиском в ширину, потому что он уровень за уровнем просматривает ближайшие клетки. Поиск в глубину выбирает какое-то одно направление и просматривает его вглубь на заданное число клеток, переходит к следующему направлению и так далее, пока не найдет конечную точку. Представленная ниже рекурсивная функция находит не все возможные пути. Чтобы найти кратчайший путь, надо вызвать эту функцию для каждой из клеток, прилегающих к начальной клетке. Во вспомогательном булевом массиве Mark такого же размера, как и остальная карта, хранится 1, если текущая клетка уже пройдена алгоритмом, и 0 — в противном случае. В переменных Destination_x и Destination_y должны храниться координаты точки, куда в итоге надо попасть. В глобальной перемененной Length будет храниться длина текущего пути, чтобы мы не залетели вглубь матрицы дальше, чем MAX_LENGTH. Procedure DepthSearch(x,y:integer); Var i : integer;
If Length>MAX_LENGTH then exit; Mark[x,y] := True; If (x=Destination_x) and (y=Destination_y) then Begin { Мы нашли эту точку! Искомый путь представлен значениями True в массиве Mark. Здесь вы можете запомнить построенный путь и очистить Mark[x,y], чтобы продолжить поиск, или же остановиться, если задачей было найти хотя бы один путь. } End; Length:=Length+1; If Mark[x+1,y]=false then DepthSearch(x+1,y); If Mark[x,y+1]=false then DepthSearch(x,y+1); If Mark[x+1,y+1]=false then DepthSearch(x+1,y+1); If Mark[x-1,y-1]=false then DepthSearch(x-1,y-1); If Mark[x-1,y]=false then DepthSearch(x-1,y); If Mark[x,y-1]=false then DepthSearch(x,y-1); Length:=Length-1; End; В некоторых случаях этот алгоритм работает быстрее, чем волновой, но у него есть свой недостаток: если точка, путь до которой надо найти, находится дальше, чем MAX_LENGTH, алгоритм ее не найдет. Можно снять это ограничение, но тогда появится опасность зацикливания. Поиск в глубину хорошо работает в случае больших лабиринтов с узкими проходами. На широких открытых пространствах лучше использовать поиск в ширину. * * * Мы рассмотрели базовые алгоритмы поиска пути. За бортом остались такие интересные вещи, как “звездный” алгоритм A*, потенциальные поля, трассировка пути и, конечно же, тонкости реализации всех этих алгоритмов в GLScene. Все эти вопросы мы обязательно затронем в одной из статей цикла. Автор: Константин Артемьев
|