Alcune idee su come evitare le ricerche che si traducono in percorsi completamente falliti:
ID isola
Uno dei modi più economici per completare in modo efficace le ricerche A * più velocemente è quello di non effettuare alcuna ricerca. Se le aree sono veramente impraticabili da parte di tutti gli agenti, riempire ogni area con un ID isola univoco al carico (o nella conduttura). Durante l'individuazione del percorso, verificare se l' ID isola dell'origine del percorso corrisponde all'ID isola della destinazione. Se non corrispondono, non ha senso effettuare la ricerca: i due punti si trovano su isole distinte e non collegate. Questo aiuta solo se ci sono nodi veramente impraticabili per tutti gli agenti.
Limite limite superiore
Limito il limite superiore del numero massimo di nodi che è possibile cercare. Ciò consente di eseguire ricerche impraticabili per sempre, ma significa che è possibile perdere alcune ricerche percorribili molto lunghe. Questo numero deve essere ottimizzato e non risolve davvero il problema, ma mitiga i costi associati alle ricerche lunghe.
Se quello che stai riscontrando è che sta impiegando troppo tempo , sono utili le seguenti tecniche:
Rendilo asincrono e limita le iterazioni
Lascia che la ricerca venga eseguita in un thread separato o un po 'ogni frame in modo che il gioco non si fermi in attesa della ricerca. Mostra l'animazione della testa che graffia il personaggio o calpesta i piedi o qualsiasi cosa sia appropriata mentre aspetti che la ricerca termini. Per fare questo in modo efficace, vorrei mantenere lo stato della ricerca come oggetto separato e consentire l'esistenza di più stati. Quando viene richiesto un percorso, afferrare un oggetto stato libero e aggiungerlo alla coda di oggetti stato attivi. Nell'aggiornamento del percorso, estrarre l'elemento attivo dalla parte anteriore della coda ed eseguire A * fino a quando A. non viene completato o B. viene eseguito un limite di iterazioni. Se completo, reinserire l'oggetto stato nell'elenco di oggetti stato liberi. Se non è stato completato, inserirlo alla fine delle "ricerche attive" e passare a quello successivo.
Scegli le giuste strutture dati
Assicurati di utilizzare le giuste strutture dati. Ecco come funziona il mio StateObject. Tutti i miei nodi sono pre-assegnati a un numero finito - diciamo 1024 o 2048 - per motivi di prestazioni. Uso un pool di nodi che accelera l'allocazione dei nodi e mi consente anche di archiviare gli indici anziché i puntatori nelle strutture dei dati che sono u16s (o u8 se ho 255 nodi max, cosa che faccio su alcuni giochi). Per il mio pathfinding uso una coda di priorità per l'elenco aperto, memorizzando i puntatori agli oggetti Node. È implementato come un heap binario e ordino i valori in virgola mobile come numeri interi poiché sono sempre positivi e la mia piattaforma ha confronti lenti in virgola mobile. Uso una tabella hash per la mia mappa chiusa per tenere traccia dei nodi che ho visitato. Memorizza NodeIDs, non Nodes, per risparmiare sulle dimensioni della cache.
Cache Cosa puoi
Quando si visita per la prima volta un nodo e si calcola la distanza dalla destinazione, memorizzarlo nella cache memorizzata nell'oggetto State. Se si rivisita il nodo, utilizzare il risultato memorizzato nella cache invece di calcolarlo di nuovo. Nel mio caso aiuta a non dover fare una radice quadrata su nodi rivisitati. Potresti scoprire che ci sono altri valori che puoi precalcolare e memorizzare nella cache.
Ulteriori aree su cui è possibile indagare: utilizzare la ricerca di percorsi bidirezionale per eseguire ricerche da entrambe le estremità. Non l'ho fatto ma, come altri hanno notato, questo potrebbe aiutare, ma non è privo di avvertimenti. L'altra cosa sulla mia lista da provare è la ricerca gerarchica di percorsi o la ricerca di percorsi di clustering. C'è una descrizione interessante nella documentazione di HavokAI Qui che descrive il loro concetto di clustering, che è diverso dalle implementazioni HPA * descritte qui .
Buona fortuna e facci sapere cosa trovi.