Basic Ai pathfinding customization in Unreal

Ai per uno zombie in Unreal Engine 4, un approccio migliore di “AiGoTo” in Blueprint

Quando si tratta di preparare strumenti per giovani sviluppatori senza esperienza, Unreal Engine 4 eccelle, è possibile infatti anche senza nessuna conoscenza di pathfinding, posizionando un semplice NavMeshBound e chiamando la funzione predefinita AiGoTo, far muovere un Ai in modo dinamico, fargli automaticamente evitare gli ostacoli, e con pochi blocchi in più, addirittura inseguire il PlayerCharacter.

Però chi ha già testato questo sistema, sa che non è il massimo. Non solo il movimento delle Ai è meccanico, irrealistico, ma inoltre se utilizzato per creare un orda di nemici, essi non faranno altro che inseguire il Player in fila indiana, diventando estremamente prevedibili, facili da evitare, e particolarmente sgraziati.

Senza andare a codificare un complesso algoritmo di pathfinding, in grado di risolvere labirinti o trovare la strada migliore per raggiungere il target, si può comunque ottenere un risultato più efficace per questo caso specifico, utilizzando delle linee tracciate dal centro dell’agente verso diverse direzioni (LineTraceByChannel), controllando la distanza tra un Ai e eventuali ostacoli, ed effettuare scelte di conseguenza.

In questo modo se diversi agenti stanno inseguendo il Player, essi non solo cercheranno di evitare gli ostacoli, ma tratteranno gli altri agenti stessi come tali, cercando di distanziarsene, formando così un’orda ampia e minacciosa.

Abbiamo riportato qua sotto la nostra soluzione, ma ci sono diversi approcci per ottenere questo effetto.

Gli ingredienti per un Ai personalizzabile

Quello che ci serve per sviluppare e testare questo algoritmo è:

  • Un livello con un piano e degli ostacoli posti su di esso
  • Un player character che spawna all’inizio della partita
  • Il Pawn del livello impostato a DefaultPawn (indicazioni più precise a seguire)
  • Un nuovo Actor vuoto di classe Character

Ecco come impostare il Pawn a DefaultPawn:

Questo ci servirà per osservare il comportamento degli agenti dall’alto, e avere una migliore visuale di cosa sta accadendo.

Bene, ora rechiamoci nel Blueprint del nostro “zombie”, che d’ora in poi chiameremo Z, e prepariamoci all’azione.

L’agente Z

Dopo aver opportunamente creato un nuovo Actor di classe Character, aggiungiamo una serie di componenti che ci serviranno più avanti.

Queste frecce saranno il punto di partenza e la direzione da cui tracciare le linee che utilizzeremo per prevedere le collisioni con eventuali ostacoli.

Da notare che le frecce sono impostate come figli di “Scene”, un componente senza effetti sul mondo di gioco ma dallo scopo di raggruppare le varie Arrow per rotazione, traslazione e scala.

Event BeginPlay

Prima di tutto, dobbiamo preoccuparci di muovere Z in una direzione neutra, ovvero diritto contro il bersaglio, quando non si hanno ancora informazioni, o la direzione ottimale non è stata ancora calcolata.

Inoltre salviamo il primo attore che compare nella lista degli attori con tag “Target”. Questo può essere cambiato più tardi inserendo una qualche logica per scegliere il bersaglio preferito dalla lista.

Infine impostiamo che la funzione di calcolo della direzione “CalcoloDirezione” che creeremo successivamente si aggiorni ogni 0,1 secondi (è possibile inserire un delay migliore ma lo script diventa più pesante, più Ai si hanno maggiore deve essere il delay).

Event Tick

Ogni frame, dovremo aggiornare la direzione di movimento di Z con quella ottenuta dalla funzione

Come debug possiamo usare un LineTraceByChannel con visibility “Per duration” che abilitiamo solo quando vogliamo vedere dove effettivamente sta andando Z. Quando non dobbiamo verificare questo dato, la visibility deve essere settata a “None”. Questo anche perché siccome possiamo impostare che Z scivoli sul terreno invece che curvare in modo rigido, così diventa più chiaro capire dove sta puntando. Inoltre se ci fossero diversi agenti Z che si incastrano tra di loro, questa traccia ci indica come mai non si sbloccano.

La funzione “Calcolo Direzione”

Partiamo dal calcolare nuovamente la direzione centrale, dopodiché controlliamo se Z è fermo

Se Z è fermo resettiamo la variabile WrongDirection, e resettiamo Direction. Questo vedremo fra poco che ci servirà per capire se Z è bloccato.

In ogni caso ruotiamo “Scene” (il componente a cui sono attaccate tutte le frecce che abbiamo aggiunto precedentemente), nella direzione di Target, in modo da effettuare i controlli a partire dalla direzione in cui desideriamo mandare Z.

Come ultimo passaggio in questa schermata, creiamo un array di tutte le frecce che useremo per verificare le collisioni nell’immediato futuro.

Per ogni freccia verifichiamo la collisione, e salviamo il risultato nella mappa “MovementDirection”.

Ok, ora dobbiamo solo scegliere la direzione migliore

Questo ultimo passaggio è un po’ complicato, ma in sostanza ci occupiamo di scegliere la direzione in cui andare prediligendo:

  • il centro, se non ci sono ostacoli
  • la direzione dove l’ostacolo non è presente più vicina al centro di Z
  • la direzione dove l’ostacolo è posto a distanza maggiore in caso ci siano ostacoli in tutte le direzioni
  • se abbiamo già una direzione salvata in memoria, e non siamo incastrati, e il centro ancora non è libero, continuiamo in quella direzione

Un piccolo video dimostrativo:

Come vedete non solo le Ai non si mettono in fila indiana per inseguire il Player, ma sono anche in grado di seguirlo mentre salta o è in volo, un’altra funzione che AiGoTo non supporta!

Da notare che per un risultato ancora migliore bisognerebbe evitare nel controllo degli ostacoli le Ai stesse quando la distanza dal Player è più alta di un certo limite (o non risultano visibili), per farli incastrare meno, arrivare più velocemente e salvare tempo di computazione.

Leonardo Bonadimani – Whatar – Filosoft

www.twitch.tv/whatartv