Деякі ідеї щодо уникнення пошукових запитів, які призводять до невдалих шляхів:
Ідентифікатор острова
Один з найдешевших способів швидшого завершення пошуку A * - це взагалі не здійснювати пошуку. Якщо райони справді непрохідні всі агенти, затоплення заповніть кожну область унікальним ідентифікаційним островом навантаження (або в трубопроводі). Під час визначення маршруту перевіряйте, чи відповідає Ідентифікатор острова походження шляху, ідентифікатор острова пункту призначення. Якщо вони не збігаються, немає сенсу проводити пошук - обидві точки знаходяться на чітких, непоєднаних островах. Це допомагає лише за наявності справді непрохідних вузлів для всіх агентів.
Обмежте верхню межу
Я обмежую верхню межу максимальної кількості вузлів, які можна шукати. Це допомагає непрохідним пошукам вестися назавжди, але це означає, що деякі прохідні пошуки, які дуже довго можуть бути втрачені, можуть бути втрачені. Це число потрібно налаштувати, і це насправді не вирішує проблему, але це зменшує витрати, пов’язані з тривалими пошуками.
Якщо ви виявляєте, що це забирає занадто довго, то такі методи корисні:
Зробіть це асинхронним та обмеженням ітерацій
Нехай пошук запускається окремим потоком або трохи кожного кадру, щоб гра не затримувалась в очікуванні пошуку. Відобразити анімацію персонажа, що дряпає голову чи тупотить ногами, або що завгодно, під час очікування завершення пошуку. Щоб зробити це ефективно, я б зберігав Державу пошуку як окремий об'єкт і дозволяв би існувати декілька станів. Коли запитується шлях, візьміть об'єкт вільного стану та додайте його до черги об'єктів активного стану. У своєму оновленні шляху прослідкування витягніть активний елемент з передньої частини черги та запустіть A * до тих пір, поки він не завершить A. або B. не запуститься деяка межа ітерацій. Якщо завершено, поверніть об'єкт стану назад у список вільних об'єктів стану. Якщо він не завершився, поставте його в кінці "активних пошуків" і перейдіть до наступного.
Виберіть правильні структури даних
Переконайтеся, що ви використовуєте правильні структури даних. Ось як працює мій StateObject. Усі мої вузли заздалегідь виділяються на кінцеве число - скажімо, 1024 або 2048 - з міркувань продуктивності. Я використовую пул вузлів, що прискорює розподіл вузлів, і він також дозволяє мені зберігати індекси замість покажчиків у моїх структурах даних, які є u16s (або u8, якщо у мене є 255 max вузлів, що я роблю в деяких іграх). Для мого пошуку маршрутів я використовую чергу пріоритетів для відкритого списку, зберігаючи покажчики на об’єкти Node. Він реалізований у вигляді двійкової купи, і я сортую значення плаваючої точки як цілі числа, оскільки вони завжди позитивні, і моя платформа має повільну порівняння з плаваючою точкою. Я використовую хештел для закритої карти, щоб відстежувати вузли, які я відвідав. Він зберігає NodeID, а не Nodes, щоб економити на розмірах кешу.
Кешуйте, що можете
Коли ви вперше відвідуєте вузол і обчислюєте відстань до пункту призначення, кешуйте його у вузлі, що зберігається в об'єкті стану. Якщо ви переглянете вузол, використовуйте кешований результат, а не обчислюючи його ще раз. У моєму випадку це допомагає не робити квадратний корінь на переглянутих вузлах. Ви можете виявити, що є інші значення, які ви можете попередньо обчислити та кешувати.
Подальші області, які ви могли б дослідити: використовуйте двосторонній пошук маршрутів для пошуку з будь-якого кінця. Я цього не робив, але, як інші зауважили, це може допомогти, але не обійтися. Інша річ у моєму списку, яку слід спробувати, - це ієрархічне наведення маршрутів або пошук кластеризації шляху. У документації HavokAI є цікавий опис Тут описується їх концепція кластеризації, яка відрізняється від описаних тут реалізацій HPA * .
Удачі, і повідомте нам, що ви знайдете.