Deze tutorial is het laatste deel in het coderen van een hockeywedstrijd met behulp van stuurgedrag en eindige toestandsmachines. Hier zullen we de kunstmatige intelligentie van onze atleten verbeteren om hen in staat te stellen hun doel te verdedigen tegen hun tegenstanders. We zullen onze atleten ook wat aanvalstactieken laten uitvoeren terwijl ze verdedigen, zodat ze de puck kunnen herstellen en het offensief van de tegenstander kunnen beëindigen.
In een competitief spel zoals hockey, is het verdedigingsproces veel meer dan alleen haasten naar het doelgebied van het team om te voorkomen dat de tegenstander scoort. Voorkomen dat de tegenstander scoorde, is slechts een van de vele taken.
Als een team alleen focust op het voorkomen van scores, worden alle atleten onderweg alleen maar obstakels. De tegenstander zal blijven duwen en proberen een plek in de verdedigingsformatie te vinden. Het zal enige tijd duren, maar uiteindelijk zal de tegenstander scoren.
Het verdedigingsproces is een mengeling van verdedigende en aanstootgevende acties. De beste manier om de aanval van de tegenstander te beëindigen, het verdedigingsdoel, is om aanvallen tijdens het verdedigen. Het klinkt misschien een beetje verwarrend, maar het is volkomen logisch.
Een atleet die zijn team verdedigt, moet zijn doel bereiken en voorkomen dat de tegenstander scoort. Onderweg moet hij proberen de puck te stelen van de tegenstander die hem draagt, of de puck onderscheppen wanneer deze wordt uitgewisseld tussen de tegenstanders. Dat is precies wat deze tutorial zal proberen te implementeren: een defensieve tactiek met aanvalsacties.
Om een defensief gedrag te bereiken met een aantal aanvalsaspecten, voegen we twee nieuwe toestanden toe aan de AI finite-state machine:
Een op een stapel gebaseerde eindige toestandsmachine die de aanval en de verdedigingsprocessen weergeeft ...De verdedigen
staat zal de fundamentele steen zijn in het verdedigingsproces. Terwijl in die staat, zullen de atleten zich naar hun kant van de ijsbaan begeven en proberen ze altijd de puck te herstellen om het aanvallende spel van de tegenstander te beëindigen..
De patrouille
staat zal het defensieproces aanvullen. Het zal voorkomen dat atleten stil blijven staan wanneer ze hun verdedigingspositie op de ijsbaan bereiken. Deze status zorgt ervoor dat atleten blijven bewegen en patrouilleren in het gebied, wat een overtuigender resultaat zal opleveren.
De verdedigen
staat is gebaseerd op een heel eenvoudig idee. Als het actief is, beweegt elke atleet zich naar zijn oorspronkelijke positie op de baan. We hebben deze positie al gebruikt, beschreven door de mInitialPosition
eigendom in de Atleet
klasse, om het prepareForMatch
staat in de eerste tutorial in deze serie.
Tijdens het bewegen naar zijn aanvankelijke positie, zal een atleet proberen om enkele aanvalsacties tegen de tegenstander uit te voeren als hij dichtbij genoeg is en de puck draagt. Bijvoorbeeld, als de atleet beweegt en de leider van de tegenstander (degene met de puck) een buurman wordt, de verdedigen
staat zal worden vervangen door iets meer geschikt, zoals de stealPuck
staat.
Omdat atleten tijdens het aanvallen meestal over de hele baan worden verspreid, wanneer ze overschakelen naar verdedigen
en beginnen terug te keren naar hun oorspronkelijke positie, ze zullen een belangrijk gebied bestrijken, en zorgen voor een overtuigend verdedigingspatroon:
Sommige atleten zullen onderweg geen tegenstanders tegenkomen, dus ze gaan gewoon naar hun oorspronkelijke positie. Andere atleten kunnen echter in de buurt komen van een aantal interessante tegenstanders, zoals de leider (degene die de puck draagt).
De verdedigen
staat heeft vier overgangen:
Drie van hen, team heeft de puck
, dicht bij de leider van de tegenstander
, en puck heeft geen eigenaar
, zijn gerelateerd aan aanvalsacties. Ze zullen ervoor verantwoordelijk zijn dat atleten hun tegenstanders aanvallen terwijl ze bewegen om het doel van het team te verdedigen. De in positie
De overgang zal worden geactiveerd wanneer de atleet uiteindelijk zijn oorspronkelijke positie op de baan bereikt.
De eerste stap bij het implementeren van de verdedigen
status is om de atleet naar zijn oorspronkelijke positie te laten bewegen. Aangezien hij moet vertragen naarmate hij dichter bij de bestemming komt, is het aankomende stuurgedrag perfect passend:
class Athlete // (...) private function defend (): void var aPuckOwner: Athlete = getPuckOwner (); // Ga naar de beginpositie en kom daar vlot aan. mBoid.steering = mBoid.steering + mBoid.arrive (mInitialPosition); // Heeft de puck een eigenaar? if (aPuckOwner! = null) // Ja, dat is het geval. Wie heeft het? if (doesMyTeamHasThePuck ()) // Mijn team heeft de puck, tijd om te stoppen met verdedigen en beginnen met aanvallen! mBrain.popState (); mBrain.pushState (attack); 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); // (… )
Het aankomgedrag creëert een kracht die de atleet naar zijn oorspronkelijke positie duwt (mInitialPosition
) Terwijl de verdedigen
staat is actief. Na de aankomstkrachtberekening, in deze code, voeren we een reeks tests uit die het eigendom van de puck en de nabijheid van tegenstanders controleren, waardoor verdedigen
vanuit de hersenen komen en een nieuwe duwen volgens de situatie.
Als de puck geen eigenaar heeft, bevindt deze zich waarschijnlijk vrij op de baan. In dat geval, de pursuePuck
toestand zal in de hersenen worden gedrukt (regel 29). Als de puck een teameigenaar heeft, betekent dit dat het verdedigingsproces voorbij is en dat het tijd is om aan te vallen (regel 16). Eindelijk als de eigenaar van de puck tot het team van de tegenstander behoort en hij dichtbij genoeg is, stealPuck
zal in de hersenen worden geduwd (lijn 22).
Het resultaat is een team dat in staat is om hun doel te verdedigen, het nastreven en proberen de puck te stelen van de tegenstander die het draagt. Hieronder is een demonstratie van de huidige verdedigingsimplementatie:
Het huidige verdedigingsgedrag is acceptabel, maar het kan een beetje worden aangepast om meer overtuigend te zijn. Als je de vorige demo analyseert, merk je mogelijk dat atleten stoppen en stilstaan nadat ze hun oorspronkelijke positie hebben bereikt tijdens de verdediging.
Als een atleet terugkeert naar zijn oorspronkelijke positie zonder tegenstanders tegen te komen, blijft hij stil totdat een tegenstander met de puck passeert of het team de puck herstelt.
We kunnen dit gedrag verbeteren door een patrouille
staat, die in de hersenen wordt geduwd door de verdedigen
aangeven wanneer de atleet zijn beginpositie bereikt:
De patrouille
staat is uiterst eenvoudig. Wanneer het actief is, zullen atleten zich gedurende een korte tijd willekeurig verplaatsen, wat visueel het verwachte gedrag nabootst van een atleet die probeert een plek op de ijsbaan te verdedigen.
Wanneer de afstand tussen de atleet en zijn beginpositie groter is dan 10
, bijvoorbeeld, patrouille
springt uit het brein en duwt verdedigen
. Als de atleet zijn oorspronkelijke positie opnieuw bereikt tijdens het verdedigen, patrouille
wordt opnieuw in de hersenen geduwd en het proces herhaalt zich:
Het willekeurige bewegingspatroon vereist door de patrouille
toestand kan gemakkelijk worden bereikt met het dwaalstuurgedrag. De implementatie van de patrouille
staat is:
klasse Atleet // (...) privéfunctiepatrouille (): void mBoid.steering = mBoid.steering + mBoid.wander (); // Ben ik te ver weg van mijn oorspronkelijke positie? if (Utils.distance (mInitialPosition, this)> 10) // Ja, dat ben ik. Het is tijd om te stoppen met patrouilleren en terug te gaan naar // mijn oorspronkelijke positie. mBrain.popState (); mBrain.pushState (verdedigen); // (...)
De afstandscontrole (regel 8) zorgt ervoor dat de atleet een klein gebied rond zijn aanvankelijke positie patrouilleert in plaats van zijn aanvankelijke verdedigingspositie volledig onbewaakt achter te laten.
De resultaten van het gebruik van de patrouille
staat is een overtuigender gedrag:
Tijdens de implementatie van de stealPuck
staat in de vorige tutorial, was er een situatie waarin sporters zouden moeten overschakelen naar de verdedigen
staat. Die staat was toen echter nog niet geïmplementeerd.
Tijdens het proberen de puck te stelen (de stealPuck
staat), als de tegenstander te ver weg is van de atleet, is het zinloos om te blijven proberen de puck te stelen. De beste optie in die situatie is om de stealPuck
staat en duwt verdedigen
, in de hoop dat een teamgenoot dichter bij de leider van de tegenstander zal staan om de puck te stelen.
De stealPuck
status moet worden gewijzigd (regels 28 en 29) om atleten toe te staan om de verdedigen
staat in die situatie:
class Athlete // (...) private function stealPuck (): void // Heeft de puck een eigenaar? if (getPuckOwner ()! = null) // Ja, dat is het, maar wie heeft het? if (doesMyTeamHasThePuck ()) // Mijn team heeft de puck, dus het is tijd om te stoppen met proberen / de puck te stelen en te gaan aanvallen. mBrain.popState (); mBrain.pushState (attack); else // Een tegenstander heeft de puck. var aOpponentLeader: Athlete = getPuckOwner (); // Zit de tegenstander met de puck dicht bij 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); // (… )
Na het updaten van de stealPuck
staat, atleten zijn nu in staat om aanval- en verdedigingstactieken te organiseren, waardoor twee AI-bestuurde teams in staat zijn om tegen elkaar te spelen.
Het resultaat wordt hieronder gedemonstreerd:
In deze zelfstudie hebben we een verdedigingstactiek geïmplementeerd die door atleten wordt gebruikt om hun doel te verdedigen tegen tegenstanders. We hebben toen de verdedigen
door enkele aanvalsacties toe te voegen, bijvoorbeeld om de puck van de tegenstander te stelen, waardoor de verdedigingstactiek natuurlijker en overtuigender werd.
We hebben ook het gevoel van het verdedigingsgedrag verbeterd door een extreem eenvoudige maar krachtige staat toe te voegen, de patrouille
. Het idee is om te voorkomen dat atleten stil blijven staan terwijl ze het doel van hun team verdedigen.
En daarmee hebben we een volledig AI-systeem voor ons hockeyspel gemaakt!