Некоторые идеи об избежании поисков, которые приводят к ошибочным путям в целом:
ID острова
Один из самых дешевых способов эффективного завершения поиска A * - вообще не выполнять поиск. Если районы действительно недоступны для всех агентов, заполните каждую область уникальным идентификатором острова под нагрузкой (или в трубопроводе). При поиске пути проверьте, соответствует ли идентификатор острова источника пути идентификатору острова пункта назначения. Если они не совпадают, поиск не имеет смысла - две точки находятся на разных, не связанных между собой островах. Это помогает только при наличии действительно непроходимых узлов для всех агентов.
Ограничить верхнюю границу
Я ограничиваю верхнюю границу максимального количества узлов, которые можно искать. Это помогает непроходимым поискам работать вечно, но это означает, что некоторые проходимые поиски, которые очень долго могут быть потеряны. Это число необходимо настроить, и оно не решает проблему, но снижает затраты, связанные с длительным поиском.
Если вы обнаружите, что это занимает слишком много времени, тогда полезны следующие методы:
Сделайте это асинхронным и ограничить итерации
Пусть поиск запускается в отдельном потоке или немного в каждом кадре, чтобы игра не застыла в ожидании поиска. Отобразите анимацию персонажа, царапающего голову или топающего ноги, или чего-либо подходящего в ожидании окончания поиска. Чтобы сделать это эффективно, я бы сохранил состояние поиска как отдельный объект и учел бы существование нескольких состояний. Когда запрашивается путь, возьмите объект свободного состояния и добавьте его в очередь активных объектов состояния. В своем обновлении поиска пути вытяните активный элемент из передней части очереди и запускайте A *, пока не будет завершено либо A., либо B. Выполнено некоторое ограничение итераций. Если завершено, поместите объект состояния обратно в список свободных объектов состояния. Если он еще не завершен, поместите его в конец «активных поисков» и переходите к следующему.
Выберите правильные структуры данных
Убедитесь, что вы используете правильные структуры данных. Вот как работает мой StateObject. Все мои узлы предварительно выделены для конечного числа - скажем, 1024 или 2048 - по соображениям производительности. Я использую пул узлов, который ускоряет распределение узлов, и это также позволяет мне хранить индексы вместо указателей в моих структурах данных, которые являются u16s (или u8, если у меня есть максимум 255 узлов, что я делаю в некоторых играх). Для поиска пути я использую приоритетную очередь для открытого списка, сохраняя указатели на объекты Node. Он реализован в виде двоичной кучи, и я сортирую значения с плавающей запятой в виде целых чисел, поскольку они всегда положительны, а моя платформа имеет медленные сравнения с плавающей запятой. Я использую хеш-таблицу для своей закрытой карты, чтобы отслеживать узлы, которые я посетил. Он сохраняет NodeID, а не Nodes, чтобы сэкономить на размерах кэша.
Кеш что можешь
Когда вы впервые посещаете узел и вычисляете расстояние до места назначения, кэшируйте его в узле, хранящемся в объекте состояния. При повторном посещении узла используйте кэшированный результат вместо его повторного вычисления. В моем случае это помогает не делать квадратный корень на повторных узлах. Вы можете обнаружить, что есть другие значения, которые вы можете рассчитать и кэшировать.
Другие области, которые вы можете исследовать: используйте двусторонний поиск пути для поиска с любого конца. Я не сделал этого, но, как другие заметили, это может помочь, но не без предостережений. Другая вещь в моем списке, чтобы попробовать это иерархический поиск пути или поиск пути кластеризации. Существует интересное описание в документации HavokAI Здесь описания их кластерную концепцию, которая отличается от HPA * реализации описанного здесь .
Удачи, и дайте нам знать, что вы найдете.