Рейтинг темы:
  • 0 Голос(ов) - 0 в среднем
  • 1
  • 2
  • 3
  • 4
  • 5
Поиск пути 2.0
#1
-[deleted]-
m0nster.art - clear client patches, linkz to utils & code.
Гадаю по капче.
Ответ
#2
Алгоритмы обхода препятствий

Навигация движения путника(unit) от пункта A в пункт B всегда была
интересной проблемой, которой интересовались программисты.
Есть различные способы реализации и я расскажу о нескольких разных
алгоритмах.


Самый короткий и истинный маршрут( волновой алгоритм )

Есть различные мнения, что считать самым коротким маршрутом между
двумя точками на карте. Я считаю, что алгоритм Djikstra's
наиболее общее известный и хорошо документированы. Этот алгоритм
основывается на взвешенных графах, где расстояние между двумя
точками берется в графе. Но в дальнейшем мы не будем рассматривать
этот алгоритм. Хотя его можно упростить и оптимизировать. Так как
в играх в большинстве случаев мы имеем дело с картами, которые
разбиты на равные секции ( ячейки, квадраты, tiles ).

Алгоритм, который я предлагаю Вашему вниманию в большой степени
основывается на алгоритме Djikstra's, и учитывает допущение, что
секции на карте равны. Это алгоритм "A-звезды" или "A*".

Пример алгоритма вычисления самого короткого маршрута.

Пусть надо пройти от A до B. Нам необходим массив направлений такого
же размера что и карта с ландшафтом. Так же необходимо иметь два
списка с координатами (X,Y) свободных секций карты. Количество
элементов в них зависит от размера карты. Представьте себе, что Вы
стоите в точке A и льете воду. Вода постепенно заполняет все
свободные секции и Вы запоминаете координаты этих секций, и то откуда
на эти секции пролилась вода. Когда вода достигнет точки B, можно
узнать откуда она пролилась на точку B, и так возвращаясь Вы
достигаете точки A.

Алгоритм:

1. Добавить позицию A в список 1.

2. Установить текущим список 1.

3. Вокруг текущей точки в текущем списке ищите секции по
специальному звездообразному шаблону: Вверх, Вправо, Вниз,
Влево, Вверх-Влево, Вверх-Вправо, Вниз-Вправо, Вниз-Влево.

4. Если Вы нашли свободную секцию на карте, укажите в
массиве направлений направление на секцию с которой была
найдена эта свободная секция. И добавьте эту секцию в
текущий список.

5. Повторяйте пункты 3-4 до тех пор пока все свободные ячейки
относительно текущей позиции не будут добавлены в текущий
список.

6. Повторяйте пункты 3-5 до тех пор, пока все координаты в текущем
списке не будут оценены.

7. Замените текущий список на другой ( инвертируются выбор текущего
списка ).

8. Повторяйте пункты 3-7 до тех пор, пока точка B не будет
достигнута.

9. Вернитесь из точки B в точку A с использованием массива
направлений по найденному маршруту.


Примечания к алгоритму "A*"

1. Скорость: это - довольно медленный алгоритм.

2. Память: надо иметь два списка и это отнимет много памяти в
зависимости от размера карты. Но можно обратный маршрут
просчитать занося все точки и в конец первого списка, но все
равно нужно иметь массив направлений. Так же можно выкинуть
секцию после просмотра относительно нее всех свободных секции.

3. Кривизна пути: Возможны различные маршруты от A до B. При этом
алгоритме Путник сначала делает шаг попрек, а потом по прямой
линии. Это не очень красиво. Программа может быть
оптимизирована, чтобы маршрут был более прямой, и при этом
не удлинялся.

4. Изменения на карте: если на карте произошли какие-то изменения
уже после того был вычислен маршрут и путник начал
передвигаться, например открылась дверь в стене или сместился
какой-то монстр. Надо снова вычислить маршрут. При этом если
изменения будут носить периодический характер, например дверь
открывается/закрывается, то путник может зациклится.


Некоторые возможные модификации алгоритма "A*"

Главное различие между моим алгоритмом и алгоритмом >"A*", то
что мой алгоритм вносит понятие приоритета при внесении свободной
секции в список.

1. Когда Вы включаете новую секцию в список, удостоверьтесь, что
она находится между началом и концом движения.

2. Когда Вы выбираете секцию из списка, найдите лучшую.

Для оценки приоритета позиции P на карте, надо добавить путь от A до
P и предполагаемый маршрут от P до B. Оценить величину приоритета
можно относительно прямой линии от A до B.

Вот примерно то, что надо сделать для изменения алгоритма "A*":

1. Кроме координат секции в списке Вы должны хранить пройденную
дистанцию от точки A.

2. Вместо перебора всех элементов в списке ищите те которые имеют
наименьший приоритет вычисленный по формуле: пройденный путь +
расстояние от секции до точки B. Расстояние вычисляется как
sqrt( (x1-x2)^2 + (y1-y2)^2 ).

3. После того, как Вы нашли этот наилучший элемент списка,
поменяйте его местами с последним элементом списка и уменьшите
счет.

4. Исследуйте соседей этого наилучшего элемента списка. Каждый из
новых элементов будет иметь приоритет на единицу выше чем
наилучший элемент. ( Это будет по другому если разная местность
есть на вашей карте ).


Примечания к модификации алгоритма "A*"

1. Скорость: Этот алгоритм проверяет только те маршруты, которые
ближе всего к цели, но другие маршруты остаются не проверенны.

2. Память: мне кажется что число рассмотренных маршрутов будет
меньше поскольку просматриваются только наиболее важные.

3. Кривизна пути: мне кажется что кривизна пути при этом алгоритме
будет меньше, чем у алгоритма "A*".

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


Еще одна модификация алгоритма "A*"

1. Можно начинать оценку маршрута из начальной и конечной точек.
Причем можно использовать один и тот же список для сохранения
свободных секций.

2. Если есть на карте препятствия, которые можно пройти, но они
требуют для своего прохождения больше времени. Можно учитывать
такие препятствия ( болота, лес ), если после внесения такой
секции в список задержать поиск относительно нее других
свободных секций.

3. В алгоритме "A*" есть списки свободных секций и карта
направлений. Их можно заменить на списки координат свободных
секций с номером этой секции, который выставляется
соответственно той секции, с которой была обнаружена эта новая
свободная секция. И картой где указываться, что данная секция была
пройдена. Так же проходим все свободные секции до
точки B. Тогда при выборе маршрута от точки B надо будет с
выбрать секцию, с которой мы пришли на нее и так до достижения
точки A.

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



Алгоритм разделяй и властвуй

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

Этот алгоритм не будет работать на пересеченной местности, но от
ухода от деревьев/болот ( мелких препятствий ) возможно он подойдет.
У этого алгоритма есть еще преимущество в том, что вы игнорируете
дальние препятствия, которые могут изменится до того как путник до
них дойдет.

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


Алгоритм поворота Креша ( Crash ) или Rotation

Наиболее простой алгоритм. Данный алгоритм оценивает путь не весь
сразу, а только на один шаг. Для этого надо провести прямую линию
между началом движения и концом, определив правила как будет
совершаться движение. Как Вы уже наверное знаете, это не очень
хороший способ. Путник ведет себя странно, выбирает не верный
маршрут и иногда его движения зацикливаются. Вот этот алгоритм :

1. Постройте прямую линию до конечной точки. Надо всегда запомнить
координаты секции в которой Путник был ( на один шаг ).
2. Если Путник встретился с препятствием он пробует правило правой
руки. Перемещайте его вправо, до тех пор пока не встретится
свободный проход. И затем двигайте его на эту секцию.
3. Повторяйте пункты 1-2 до достижения конечного пункта движения.
Или если Путник оказался в предыдущей секции.
4. Если Путник попал в предыдущую секции, надо сменить правило
правой руки на правило левой руки и повторить всю процедуру
снова.

Последняя точка может быть изменена для того чтобы не было возврата
на предыдущие секции. Тогда можно пользоваться алгоритмом правой
руки во все время движения.

Алгоритм не очень хорош и не всегда отрабатывает. Особенно когда
препятствие имеет U-форму.


Дополнение к алгоритму поворота Креша

Выше рассмотренный алгоритм может быть расширен для того чтобы сделать
поведение Путника немножко более похоже на человека обходящего
препятствие. Рассмотрим пример:

########
########### A
###########
###########
###########
#######

B

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

Немного модифицируя алгоритм мы сможем решить какое правило мы должны
использовать в начале. Представьте себе что вы хотите обойти
препятствие. Вы возможно думаете так: пойду по краю препятствия до
тех пор пока не найду угол, а затем пойду к точке B. Отлично, Вы
выбрали правило левой руки.

Альтернативу между правилом правой руки и левой мы найдем при встрече
с первым же препятствием. То правило, которое даст свободный маршрут
можно будет и использовать в дальнейшем. Это конечно не будет
работать на пересеченной местности.


Расчеты до начала движения

Можно оценить весь путь от A до B до начала движения. Так же можно
выполнять задержку движения путника при просчете маршрута, чтобы
просчитать различные алгоритмы и найти наиболее оптимальный маршрут,
или не найти его вообще.

Тем не менее вот несколько соображений :

1. Память : этот способ отнимет больше памяти для каждого путника,
так как вы не захотите останавливать игру при оценке маршрута.
Вы должны иметь структуру которая сохраняет оценку маршрута,
чтобы можно было распределить вычисления на несколько раз. Эта
структура не должна сохранять каждую координату, а скорее
направление движения. Тогда потребуется 4 бита на координату.

2. Время : я не думаю, что простой путника будет большим.

3. Изменения на карте : любой алгоритм, который оценивает маршрут
не сразу, а по частям будет опускать все изменения на карте
после начала движения. Переоценка маршрута после начала движения
не является оптимальной.


Оценка маршрута с начальной точки движения и с конечной

Используя упрощенную версию алгоритма поворота Креша попробуйте четыре
маршрута при этом. Первый использует алгоритм левой руки от точки A
до B, другой правой, а два других такие же правила, но от B до A.
Один из них который достигнет цели и будет использоваться. Это не
займет много времени для оценки. Используйте возможность распределить
вычисления по времени.

За основу взята статья с http://pmg.org.ru/ai/navigato.htm , брал ее за эталон для реализации алгоритма патфайнда.
Хотя уже довольно много интересных идей написаны на С и в свободном доступе для программистов. Прикрутить не особо трудно.
Ответ
#3
На первом курсе делал поиск пути в "лабиринте". Волновым алгоритмом.
Интересная штука Smile
Ответ
#4
Array Написал:На первом курсе делал поиск пути в "лабиринте". Волновым алгоритмом.
Интересная штука Smile

Ну волновой алгоритм довольно расходная штука, особенно если запихать в mmorpg-сервер алгоритм Ли или например делать поиск волной на огромной территории, где мало препятствий...
m0nster.art - clear client patches, linkz to utils & code.
Гадаю по капче.
Ответ
#5
Ну да, неэкономный способ. Но это простительно, так как было в обучающих целях, и тестовые лабиринты были густо населены препятствиями.
Ответ
#6
Откопал статью по патчфинду:
Читать: Тыкс
Скачать (статью): mediafire
Описаны формулы по которым ищется путь, к тому же разобрано несколько алгоритмов и приведено их время работы.
Ну и напоследок тоже интересная статья (осторожно, английский!).
m0nster.art - clear client patches, linkz to utils & code.
Гадаю по капче.
Ответ
#7
Объект пометить как точкой и каждому объекту задать собственный радиус и проверку, что при перемещении из А в В нужно обходить на какое то расстояние помимо радиуса. Не буду в мелочах описывать, но думаю вы поняли что я говорю.
Ответ
#8
KaraMara Написал:Объект пометить как точкой и каждому объекту задать собственный радиус и проверку, что при перемещении из А в В нужно обходить на какое то расстояние помимо радиуса. Не буду в мелочах описывать, но думаю вы поняли что я говорю.

А если те же преграды у нас хранятся, как примитивы и их довольно много (и у них далеко не круговая форма, а допустим многоугольник)? Слишком много памяти откушает такой способ, уж лучше сделать побольше примитивов (точек, которые заблокированы), чтобы покрыть нужный радиус.
m0nster.art - clear client patches, linkz to utils & code.
Гадаю по капче.
Ответ


Перейти к форуму:


Пользователи, просматривающие эту тему: 2 Гость(ей)