Questo tutorial è la parte finale del processo di codifica di un gioco di hockey che utilizza comportamenti di guida e macchine a stati finiti. Qui, miglioreremo l'intelligenza artificiale dei nostri atleti per consentire loro di difendere il loro obiettivo contro i loro avversari. Faremo anche i nostri atleti eseguire alcune tattiche di attacco mentre stanno difendendo, in modo che possano recuperare il disco e terminare l'offensiva dell'avversario.
In un gioco competitivo come l'hockey, il processo difensivo è molto più di una semplice corsa verso l'area obiettivo della squadra per impedire all'avversario di segnare. Impedire all'avversario di segnare è solo una delle molte attività coinvolte.
Se una squadra si concentra esclusivamente sulle tattiche di prevenzione del punteggio, tutti gli atleti diventeranno solo degli ostacoli lungo il percorso. L'avversario continuerà a spingere, cercando di trovare un punto nella formazione difensiva. Ci vorrà del tempo, ma alla fine l'avversario segnerà.
Il processo di difesa è un misto di azioni difensive e offensive. Il modo migliore per terminare l'attacco dell'avversario, che è l'obiettivo della difesa, è quello di attaccare mentre difendi. Potrebbe sembrare un po 'confuso, ma ha perfettamente senso.
Un atleta che difende la sua squadra dovrebbe spostarsi verso il suo obiettivo, impedendo all'avversario di segnare. Lungo la strada, deve cercare di rubare il disco dall'avversario che lo sta trasportando, o intercettare il disco quando viene scambiato tra gli avversari. Questo è esattamente ciò che questo tutorial proverà ad implementare: una tattica difensiva con azioni d'attacco.
Per ottenere un comportamento difensivo con alcuni aspetti di attacco, aggiungeremo due nuovi stati alla macchina a stati finiti AI:
Una macchina a stati finiti basata sullo stack che rappresenta l'attacco e i processi di difesa ...Il difendere
lo stato sarà la pietra fondamentale nel processo di difesa. Mentre si trovano in quello stato, gli atleti si sposteranno verso la loro parte della pista, cercando sempre di recuperare il disco per terminare l'offensiva dell'avversario.
Il pattuglia
stato completerà il processo di difesa. Impedirà agli atleti di rimanere fermi quando raggiungeranno la loro posizione di difesa sulla pista. Questo stato manterrà gli atleti in movimento e pattugliano l'area, il che produrrà un risultato più convincente.
Il difendere
lo stato si basa su un'idea molto semplice. Quando è attivo, ogni atleta si sposterà verso la loro posizione iniziale nella pista. Abbiamo già usato questa posizione, descritta dal mInitialPosition
proprietà nel Atleta
classe, per implementare il prepareForMatch
stato nel primo tutorial di questa serie.
Mentre si muove verso la sua posizione iniziale, un atleta proverà a eseguire alcune azioni di attacco contro l'avversario se è abbastanza vicino e sta portando il disco. Ad esempio, se l'atleta si muove e il leader dell'avversario (quello con il disco) diventa un vicino, il difendere
lo stato sarà sostituito con qualcosa di più appropriato, come il stealPuck
stato.
Dal momento che gli atleti tendono ad essere diffusi attraverso l'intera pista mentre attaccano, quando passano a difendere
e iniziano a tornare alla loro posizione iniziale, copriranno un'area significativa, assicurando un modello di difesa convincente:
Alcuni atleti non incontreranno gli avversari lungo la strada, quindi andranno semplicemente verso la loro posizione iniziale. Altri atleti, tuttavia, potrebbero avvicinarsi ad alcuni avversari interessanti, come ad esempio il leader (quello che porta il disco).
Il difendere
lo stato avrà quattro transizioni:
Tre di loro, la squadra ha il disco
, vicino al leader avversario
, e puck non ha proprietario
, sono legati alle azioni di attacco. Saranno responsabili per far sembrare gli atleti che stanno attaccando gli avversari mentre si muovono per difendere l'obiettivo della squadra. Il in posizione
la transizione verrà attivata quando l'atleta arriva alla sua posizione iniziale nella pista.
Il primo passo per implementare il difendere
lo stato è quello di far muovere l'atleta verso la sua posizione iniziale. Poiché deve rallentare man mano che si avvicina alla destinazione, il comportamento di sterzata in arrivo è perfetto:
class Athlete // (...) private function defend (): void var aPuckOwner: Athlete = getPuckOwner (); // Spostati verso la posizione iniziale, arrivando senza intoppi. mBoid.steering = mBoid.steering + mBoid.arrive (mInitialPosition); // Il disco ha un proprietario? if (aPuckOwner! = null) // Sì, lo è. Chi ce l'ha? if (doesMyTeamHasThePuck ()) // La mia squadra ha il disco, il tempo di fermare la difesa e iniziare ad attaccare! mBrain.popState (); mBrain.pushState (attacco); else if (Utils.distance (aPuckOwner, this) < 150) // An opponent has the puck and he is close to us! // Let's try to steal the puck from him. mBrain.popState(); mBrain.pushState(stealPuck); else // No, the puck has no owner, it is running in the rink. // There is no point to keep defending the goal, because nobody has the puck. // Let's switch to 'pursuePuck' and try to get the puck to our team. mBrain.popState(); mBrain.pushState(pursuePuck); // (… )
Il comportamento di arrivo creerà una forza che spingerà l'atleta verso la sua posizione iniziale (mInitialPosition
) mentre il difendere
stato è attivo. Dopo il calcolo della forza di arrivo, in questo codice, eseguiamo una sequenza di test che controllerà la proprietà del disco e la vicinanza degli avversari, facendo scattare il difendere
dichiari dal cervello e spingendone uno nuovo secondo la situazione.
Se il disco non ha il proprietario, probabilmente si muove liberamente nella pista. In tal caso, il pursuePuck
lo stato sarà spinto nel cervello (linea 29). Se il disco ha un proprietario del team, significa che il processo di difesa è finito ed è ora di attaccare (linea 16). Infine se il proprietario del puck appartiene alla squadra avversaria e lui è abbastanza vicino, stealPuck
sarà spinto nel cervello (linea 22).
Il risultato è una squadra che è in grado di difendere il proprio obiettivo, inseguendo e cercando di rubare il disco all'avversario portandolo. Di seguito è riportata una dimostrazione dell'attuale implementazione della difesa:
L'attuale comportamento di difesa è accettabile, ma può essere ottimizzato un po 'per essere più convincente. Se analizzi la demo precedente, alla fine potresti notare che gli atleti si fermeranno e si fermeranno dopo aver raggiunto la loro posizione iniziale durante la difesa.
Se un atleta ritorna nella sua posizione iniziale senza incontrare alcun avversario lungo il percorso, rimarrà fermo fino a quando un avversario con il disco non passa o la squadra recupera il disco.
Possiamo migliorare questo comportamento aggiungendo a pattuglia
stato, che viene spinto nel cervello dal difendere
indica quando l'atleta raggiunge la sua posizione iniziale:
Il pattuglia
lo stato è estremamente semplice. Quando è attivo, farà in modo che gli atleti si spostino casualmente per un breve periodo, il che simula visivamente il comportamento atteso di un atleta che cerca di difendere un posto nella pista di pattinaggio.
Quando la distanza tra l'atleta e la sua posizione iniziale è maggiore di 10
, per esempio, pattuglia
si apre dal cervello e spinge difendere
. Se l'atleta arriva di nuovo nella sua posizione iniziale durante la difesa, pattuglia
viene spinto ancora una volta nel cervello e il processo si ripete:
Il modello di movimento casuale richiesto dal pattuglia
lo stato può essere facilmente raggiunto con il comportamento dello sterzo. L'implementazione del pattuglia
lo stato è:
class Athlete // (...) private function patrol (): void mBoid.steering = mBoid.steering + mBoid.wander (); // Sono troppo lontano dalla mia posizione iniziale? if (Utils.distance (mInitialPosition, this)> 10) // Sì, lo sono. È ora di smettere di pattugliare e tornare alla // posizione iniziale. mBrain.popState (); mBrain.pushState (difendere); // (...)
Il controllo della distanza (linea 8) assicura che l'atleta pattuglia una piccola area intorno alla sua posizione iniziale invece di lasciare la sua posizione di difesa iniziale completamente incustodita.
I risultati dell'uso di pattuglia
lo stato è un comportamento più convincente:
Durante l'implementazione del stealPuck
stato nel tutorial precedente, c'era una situazione in cui gli atleti dovevano passare al difendere
stato. Tuttavia questo stato non è stato implementato allora.
Durante il tentativo di rubare il disco (il stealPuck
stato), se l'avversario è troppo lontano dall'atleta, è inutile continuare a cercare di rubare il disco. La migliore opzione in questa situazione è di far apparire il stealPuck
stato e spingere difendere
, sperando che un compagno di squadra sarà più vicino al leader dell'avversario per rubare il disco.
Il stealPuck
lo stato deve essere cambiato (righe 28 e 29) per consentire agli atleti di spingere il difendere
stato in questa situazione:
class Athlete // (...) private function stealPuck (): void // Il disco ha qualche proprietario? if (getPuckOwner ()! = null) // Sì, lo ha, ma chi ce l'ha? if (doesMyTeamHasThePuck ()) // La mia squadra ha il disco, quindi è il momento di smettere di cercare di rubare // il disco e iniziare ad attaccare. mBrain.popState (); mBrain.pushState (attacco); else // Un avversario ha il disco. var aOpponentLeader: Athlete = getPuckOwner (); // L'avversario con il disco è vicino a me? if (Utils.distance (aOpponentLeader, this) < 150) // Yeah, he is close! Let's pursue him while mantaining a certain // separation from the others to avoid that everybody will ocuppy the same // position in the pursuit. mBoid.steering = mBoid.steering + mBoid.pursuit(aOpponentLeader.boid); mBoid.steering = mBoid.steering + mBoid.separation(50); else // No, he is too far away. Let's switch to 'defend' and hope // someone closer to the puck can steal it for us. mBrain.popState(); mBrain.pushState(defend); else // The puck has no owner, it is probably running freely in the rink. // There is no point to keep trying to steal it, so let's finish the 'stealPuck' state // and switch to 'pursuePuck'. mBrain.popState(); mBrain.pushState(pursuePuck); // (… )
Dopo aver aggiornato il stealPuck
stato, gli atleti sono ora in grado di organizzare tattiche di attacco e difesa, rendendo due squadre controllate dall'IA in grado di giocare l'una contro l'altra.
Il risultato è dimostrato di seguito:
In questo tutorial, abbiamo implementato una tattica di difesa utilizzata dagli atleti per difendere il loro obiettivo dagli avversari. Abbiamo quindi migliorato il difendere
dichiari aggiungendo alcune azioni di attacco, come tentare di rubare il disco dell'avversario, il che ha reso la tattica di difesa più naturale e convincente.
Abbiamo anche migliorato la sensazione del comportamento di difesa aggiungendo uno stato estremamente semplice, ma potente, il pattuglia
. L'idea è di impedire agli atleti di rimanere fermi mentre difendono l'obiettivo della loro squadra.
E con questo, abbiamo creato un sistema AI completo per il nostro gioco di hockey!