Эффективно выискивая множество врагов вокруг препятствий

20

Я работаю над тем, чтобы улучшить поиск врагов моей игры. Прямо сейчас они просто постоянно движутся к точной позиции игрока, вычисляя угол между собой и игроками и двигаясь в этом направлении. У меня также есть алгоритм флокирования, который не дает врагам складываться друг на друга, поэтому они будут объединяться в группы, а не пересекаются друг с другом.

Однако теперь, когда я добавил карту на основе тайлов, мне нужно, чтобы враги также могли обходить препятствия и стены, например. Сначала я попытался добавить значение разделения к «не проходимым» плиткам, чтобы алгоритм флокирования рассматривал стены и препятствия как объекты, от которых нужно отойти. Мне еще предстоит выяснить, возможно ли это, потому что мой первоначальный тест показал, что враги попадают в невидимую «стену», где нет неприступных тайлов, но по какой-то причине они поражают ее и начинают разбрасывать.

Мне было интересно, не слишком ли сложно вычислить путь к игроку, используя A *, а затем использовать алгоритм флокирования, чтобы предотвратить комкование. Первоначально моя игра собиралась быть стрелялкой на основе волн, но вместо этого я решил сделать ее ориентированной на уровни в духе Hotline Miami, так что, скорее всего, у меня будет меньше врагов со случайной ордой, и я просто сделаю их сильнее.

Это жизнеспособное решение? Я использую Java с Slick2D в качестве игрового движка. Или есть лучшее решение / алгоритм, который решает обе эти проблемы?

Дарин Бодро
источник
7
Как я описал в редактировании, «это слишком тяжело» - вопрос, который нужно задать вашему профилировщику, потому что это будет зависеть от вашей реализации, целевого оборудования, бюджета производительности и контекста вашей игры - всего того, что вы и ваш профилировщик знаете. близко, но интернет-незнакомцы не делают. Если вы хотите эффективно определять пути к стаду, мы можем предложить стратегии, которые помогут с этим, но только ваше собственное профилирование может ответить на то, что достаточно эффективно для ваших нужд. Если вы регистрируете и определяете конкретную проблему с производительностью, мы также можем помочь вам найти способ ее решения.
DMGregory
1
То, как вы их реализуете, влияет на производительность. Например, запускать A * только на лидерах и полагаться на фолкинг для последователей.
Пикалек
Если ваша игра в основном основана на борьбе с этими врагами, алгоритм, который вы выберете, окажет огромное влияние на то, как игра выглядит. Таким образом, вы должны попробовать разные подходы, например, кажется ли вам, что враги всегда прекрасно знают уровень и положение игрока, и они отслеживают его так, как ему руководит всезнающий ИИ? - другие подходы могут заключаться в том, чтобы позволить врагам бежать в общем направлении, где игрок шуметь, и только на прямой видимости бегут к нему, или кричать и сообщать другим врагам, где находится игрок ...
Falco
@Falco Поскольку игра больше не основана на волнах и будет основана на уровнях, а враги - зомби ... Я подумывал сделать это так, чтобы вас либо видели, либо шумили, чтобы они вас нашли. Так что, если вы используете шумное оружие? Он издает звук в радиусе действия, и все враги в радиусе действия направляются к месту расположения испускаемого звука, а затем случайным образом облетают эту область.
Дарин Бодро

Ответы:

49

Это звучит как сценарий использования полей потока.

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

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

Это создает поле потока: справочную таблицу, которая связывает каждую ячейку со следующим шагом по направлению к ближайшему объекту игрока из этой позиции.

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

Это масштабируется все лучше и лучше, чем больше врагов в вашей пастве. Для одного врага это дороже, чем A *, потому что он ищет всю карту (хотя вы можете заблаговременно, когда вы доберетесь до всех агентов, ищущих пути). Но когда вы добавляете больше врагов, они получают все больше и больше затрат на поиск пути, вычисляя сегменты общего пути один раз, а не снова и снова. Вы также получаете преимущество от того факта, что BFS / Dijkdtra проще, чем A *, и, как правило, дешевле оценивать для каждой проверяемой ячейки.

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

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

Д.М.Григорий
источник
Эта техника звучит очень аккуратно. Я проверю это.
Дарин Бодро
15
Можно использовать гибридный алгоритм, который создает поле частичного потока, не ища больше карты, чем повторные вызовы A *, и никогда не ища одну и ту же позицию дважды. Основная идея состоит в том, чтобы выбрать произвольного врага и начать поиск A * от игрока к этому врагу, отмечая ячейки, когда вы встречаете их, как при генерации поля нормального потока. Как только поиск обнаружит этого врага, выберите другого врага (которого вы еще не нашли) в качестве цели, пересортируйте открытый набор в соответствии с новой эвристикой и продолжите поиск. Остановись, когда найдешь всех врагов.
Ильмари Каронен
1
Как насчет предотвращения столкновений? Это (несколько) упомянуто в OP (избегая отсечения, когда они достигают игрока). Мне кажется, вам придется перезапускать полные джикстры каждый раз, когда что-то двигается (или добавлять дополнительную логику)
Марс
2
@Mars ОП говорит о флокировании, поэтому я предполагаю, что все люди могут двигаться с одинаковой скоростью; единственные места, где столкновения будут проблемой, - это узкие места, которые требуют от некоторых стад, чтобы остановиться и ждать. Тем не менее, на самом деле не нужно менять указание пути - простая очередь, вероятно, будет работать достаточно хорошо в большинстве случаев, и некоторое смещение пути (некоторый псевдослучайный выбор альтернативных путей с аналогичными затратами) будет работать для создания более естественного вида стада. потоки, которые также избегают целого стада, пытающегося пройти один конкретный разрыв в одной клетке :)
Луаан
3
@Luaan В игре на основе плитки вы будете удивлены, как часто происходят столкновения. Лично я нахожу опцию «очередей» менее оптимальной. Кроме того, если юниты не могут проходить друг через друга, вам нужно будет пересчитать, когда юниты начнут входить в свою конечную позицию, и множество других крайних случаев. Флокирование сложно;)
Марс
8

A * не тяжелая производительность. Я бы подошел к этой ситуации, варьируя алгоритмы. Делайте A * время от времени, и между ними проверьте, свободен ли следующий шаг или вам нужно уклонение.

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

Лучше всего поработать с конечными автоматами здесь и почитать книгу Мэтта Бакленда "Программирование AI на примере". Книга предлагает проверенные методы для вашей проблемы и детали математики требуется. Исходный код из книги доступен в Интернете; книга написана на C ++, но некоторые переводы (включая Java) доступны.

D3d_dev
источник
2
При нечасто обновляющемся подходе A * может быть полезно пошатнуть ваши обновления, сохраняя бюджет на то, сколько врагов может перенаправить путь в одном кадре. Таким образом, вы можете сохранить максимальную стоимость поиска пути на один кадр и более надежно обрабатывать многие пути искусственного интеллекта, амортизируя их общую стоимость в течение нескольких кадров. ИИ, использующий устаревший путь для одного или двух кадров, когда бюджет на кадр был превышен, или откат на мертвый расчет, если он близок, обычно не будет разрушительным.
DMGregory
2
Вероятно, здесь указывается очевидное, но если вы собираетесь обновлять только некоторые из ваших путей в данном кадре, вам может потребоваться система приоритетов, основанная на расстоянии до игрока. Вероятно, для врагов рядом с игроком важнее обновить свои пути, в то время как для врагов, находящихся далеко, возможно, использовать устаревший путь.
AC
4

Мало того, что это осуществимо, я считаю, что это было сделано в коммерческой игре в 90-х годах - BattleZone (1998).

В этой игре были трехмерные юниты со свободным движением, не основанным на тайлах, и базовая конструкция на основе тайлов.

Вот как это работает:

Во-первых, A * или что-то подобное (вероятно, вариация A * со строгими ограничениями на длину пути, который он может найти, поэтому он никогда не требует слишком много ресурсов для запуска, но не всегда находит путь до места назначения) будет использоваться, чтобы найти путь для hovertank, чтобы добраться до его пункта назначения, не застревая в основанных на плитке препятствиях.

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

Robyn
источник
1
Так что же является хорошим способом справиться с этим путем, но не совсем? Если я позволю деткам поворачивать на поворотах, я должен быть в состоянии предотвратить столкновение врагов с углом препятствия. Должен ли я сохранять поведение флоков как для врагов, так и для препятствий, и добавить A *, чтобы справиться с этими ситуациями?
Дарин Бодро