Inzicht in stuurgedrag botsingsvermijding

Goede NPC-navigatie vereist vaak het vermogen om obstakels te vermijden. Deze tutorial behandelt de het uit de weg gaan van botsingen stuurgedrag, waardoor personages gracieus allerlei obstakels in de omgeving kunnen ontwijken.

Notitie: Hoewel deze tutorial geschreven is met behulp van AS3 en Flash, zou je in bijna elke game-ontwikkelomgeving dezelfde technieken en concepten moeten kunnen gebruiken. Je moet een basiskennis hebben van wiskundige vectoren.


Invoering

Het basisidee achter het vermijden van botsingen is om een ​​stuurkracht te genereren om obstakels te ontwijken telkens als er een dichtbij genoeg is om de doorgang te blokkeren. Zelfs als de omgeving meerdere obstakels heeft, gebruikt dit gedrag een van hen tegelijkertijd om de ontwijkingskracht te berekenen.

Alleen de obstakels die voor het personage liggen, worden geanalyseerd; de dichtstbijzijnde, naar men zegt de meest bedreigende, wordt geselecteerd voor evaluatie. Het resultaat is dat het personage alle obstakels in het gebied kan ontwijken en elegant en naadloos van de ene naar de andere kan overschakelen.


Obstakels voor het personage worden geanalyseerd en de dichtstbijzijnde (meest bedreigende) wordt geselecteerd.

Het botsingsvermijdingsgedrag is geen padzoekalgoritme. Het zorgt ervoor dat personages zich door de omgeving verplaatsen, obstakels vermijden en uiteindelijk een route vinden om door de blokken te gaan - maar het werkt niet echt goed met "L" of "T" obstakels, bijvoorbeeld.

Tip: Dit gedrag om botsingen te vermijden lijkt misschien op het vluchtgedrag, maar er is een belangrijk verschil tussen beide. Een personage dat in de buurt van een muur komt, vermijdt het alleen als het de weg blokkeert, maar het vlieggedrag zal het personage altijd van de muur duwen.

Vooruit zien

De eerste stap om obstakels in de omgeving te vermijden, is ze waar te nemen. De enige obstakels waar het personage zich zorgen over moet maken, zijn degenen die ervoor staan ​​en die de huidige route direct blokkeren.

Zoals eerder uitgelegd, beschrijft de snelheidsvector de richting van het personage. Het zal worden gebruikt om een ​​nieuwe vector te produceren genaamd verder, wat een kopie is van de snelheidsvector, maar met een andere lengte:


De verder vector is de gezichtslijn van het personage.

Deze vector wordt als volgt berekend:

 vooruit = positie + normaliseren (snelheid) * MAX_SEE_AHEAD

De verder vectorlengte (aangepast met MAX_SEE_AHEAD) definieert hoe ver het personage "ziet".

Hoe groter MAX_SEE_AHEAD is, hoe eerder het karakter begint te handelen om een ​​obstakel te ontwijken, omdat het het als een bedreiging zal zien, zelfs als het ver weg is:


Hoe groter de voorsprong vooruit is, hoe eerder het personage begint te handelen om een ​​obstakel te ontwijken.

Controleren op botsing

Om te controleren op een botsing, moet elk obstakel (of zijn begrenzingsvak) worden beschreven als een geometrische vorm. Het gebruik van een bol (cirkel in twee dimensies) geeft de beste resultaten, zodat elk obstakel in de omgeving als zodanig wordt beschreven.

Een mogelijke oplossing om te controleren op een botsing is het snijpunt van de lijnbol - de lijn is de verder vector en de bol is het obstakel. Die aanpak werkt, maar ik ga een vereenvoudiging gebruiken van dat wat makkelijker te begrijpen is en vergelijkbare resultaten heeft (soms zelfs betere).

De verder vector zal worden gebruikt om een ​​andere vector te produceren met de helft van de lengte:


Dezelfde richting, de helft van de lengte.

De ahead2 vector is precies zoals berekend verder, maar de lengte ervan wordt gehalveerd:

 vooruit = positie + normaliseren (snelheid) * MAX_SEE_AHEAD vooruit2 = positie + normaliseren (snelheid) * MAX_SEE_AHEAD * 0,5

We willen een botsingcontrole uitvoeren om te testen of een van deze twee vectoren zich binnen de hindernisbol bevindt. Dat wordt eenvoudig bereikt door de afstand tussen het uiteinde van de vector en het midden van de bol te vergelijken.

Als de afstand kleiner is dan of gelijk is aan de bolstraal, bevindt de vector zich in de bol en is een botsing gevonden:


De vooruitgaande vector onderschept het obstakel als d < r. The ahead2 vector was omitted for clarity.

Als een van beide van de twee voorste vectoren bevinden zich binnen de hindernisbol, dan blokkeert dat obstakel de weg. De euclidische afstand tussen twee punten kan worden gebruikt:

 persoonlijke functie afstand (a: Object, b: Object): Number return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));  private function lineIntersectsCircle (ahead: Vector3D, ahead2: Vector3D, obstacle: Circle): Boolean // de eigenschap "center" van het obstakel is een Vector3D. terugkeer afstand (obstacle.center, vooruit) <= obstacle.radius || distance(obstacle.center, ahead2) <= obstacle.radius; 

Als meer dan één obstakel de weg blokkeert, wordt de dichtstbijzijnde (de "meest bedreigende") geselecteerd voor berekening:


Het dichtstbijzijnde obstakel (meest bedreigend) wordt geselecteerd voor berekening.

Bereken de Force Force

De ontwijkingskracht moet het karakter weg van het obstakel duwen, waardoor het de bol kan ontwijken. Het kan worden gedaan met behulp van een vector die wordt gevormd door het middelpunt van de bol te gebruiken (die een positievector is) en de verder vector. We berekenen deze ontwijkingskracht als volgt:

 avoidance_force = ahead - obstacle_center avoidance_force = normalize (avoidance_force) * MAX_AVOID_FORCE

Na avoidance_force wordt berekend dat het wordt genormaliseerd en geschaald door MAX_AVOID_FORCE, dat is een nummer dat wordt gebruikt om het te definiëren avoidance_force lengte. Hoe groter MAX_AVOID_FORCE is, des te sterker is de ontwijkingskracht die het personage weg duwt van het obstakel.


Vermijdende kracht berekening. De gestreepte oranje lijn toont het pad dat het personage moet volgen om het obstakel te vermijden. Tip: De positie van een entiteit kan worden beschreven als een vector, zodat ze kunnen worden gebruikt in berekeningen met andere vectoren en krachten.

De hindernis vermijden

De uiteindelijke implementatie voor de het uit de weg gaan van botsingen() methode, die de ontwijkingskracht teruggeeft, is:

 persoonlijke functie collisionAvidance (): Vector3D ahead = ...; // bereken de toekomstige vector ahead2 = ...; // bereken de ahead2 vector var mostThreating: Obstacle = findMostThreateningObstacle (); var avoidance: Vector3D = new Vector3D (0, 0, 0); if (mostThreating! = null) avoidance.x = ahead.x - mostThreatening.center.x; avoidance.y = ahead.y - mostThreatening.center.y; avoidance.normalize (); avoidance.scaleBy (MAX_AVOID_FORCE);  else avoidance.scaleBy (0); // vernietig de ontwijkingskracht terugkeervermijding;  private function findMostThreateningObstacle (): Obstacle var mostThreating: Obstacle = null; for (var i: int = 0; i < Game.instance.obstacles.length; i++)  var obstacle :Obstacle = Game.instance.obstacles[i]; var collision :Boolean = lineIntersecsCircle(ahead, ahead2, obstacle); // "position" is the character's current position if (collision && (mostThreatening == null || distance(position, obstacle) < distance(position, mostThreatening)))  mostThreatening = obstacle;   return mostThreatening; 

De ontwijkingskracht moet worden toegevoegd aan de snelheidsvector van het personage. Zoals eerder uitgelegd, kunnen alle stuurkrachten worden gecombineerd tot één, waardoor een kracht wordt geproduceerd die alle actieve gedrag weergeeft dat op het personage inwerkt.

Afhankelijk van de hoek en richting van de vermijdingskracht worden andere stuurkrachten, zoals zoeken of afdwalen, niet onderbroken. De ontwijkingskracht wordt zoals gebruikelijk aan de afspeelsnelheid toegevoegd:

 besturing = niets (); // de nulvector, wat betekent "nulkrachtmagnitude" besturing = sturen + zoeken (); // ervan uitgaande dat het personage iets zoekt dat stuurt = sturen + botsingAvoidance (); sturen = afbreken (sturen, max_kracht) sturen = sturen / massasnelheid = afbreken (snelheid + sturen, max_snelheid) positie = positie + snelheid

Aangezien alle besturingsgedragingen elke game-update opnieuw berekenen, blijft de ontwijkingskracht actief zolang het obstakel de weg blokkeert.

Zodra het obstakel de verder vectorlijn, wordt de ontwijkingskracht nul (geen effect) of wordt deze herberekend om het nieuwe dreigende obstakel te vermijden. Het resultaat is een personage dat obstakels kan ontwijken:


Verplaats de muiscursor. Klik om krachten te tonen.

Het verbeteren van Collision Detection

De huidige implementatie heeft twee problemen, beide gerelateerd aan de botsingsdetectie. De eerste gebeurt wanneer het verder vectoren bevinden zich buiten de hindernisbol, maar het personage bevindt zich te dicht bij (of binnen) het obstakel.

Als dat gebeurt, zal het personage het obstakel raken (of binnengaan) en het vermijdingsproces overslaan omdat er geen botsing werd gedetecteerd:


Soms het verder vectoren bevinden zich buiten het obstakel, maar het personage bevindt zich binnenin.

Dit probleem kan worden opgelost door een derde vector toe te voegen aan de collisioncheck: de positievector van het personage. Het gebruik van drie vectoren verbetert de botsingsdetectie aanzienlijk.

Het tweede probleem doet zich voor wanneer het personage zich dicht bij het obstakel bevindt en er vanaf weggaat. Soms leidt het manoeuvreren tot een botsing, hoewel het personage alleen maar draait om een ​​andere richting op te gaan:


Manoeuvreren kan een botsing veroorzaken, ook al draait het personage alleen maar.

Dat probleem kan worden opgelost door de verder vectoren volgens de huidige snelheid van het personage. De code om het te berekenen verder vector, bijvoorbeeld, is veranderd in:

 dynamic_length = lengte (velocity) / MAX_VELOCITY ahead = position + normalize (velocity) * dynamic_length

De variabele dynamic_length zal variëren van 0 tot 1. Wanneer het personage op volle snelheid beweegt, dynamic_length is 1; wanneer het personage vertraagt ​​of versnelt, dynamic_length is 0 of groter (bijvoorbeeld 0,5).

Als gevolg hiervan, als het personage gewoon manoeuvreert zonder te bewegen, dynamic_length neigt naar nul, produceert een nul verder vector, die geen botsingen heeft.

Hieronder is het resultaat met deze verbeteringen:


Verplaats de muiscursor. Klik om krachten te tonen.

Demo: Het is Zombie Time!

Om het botsingsvermijdingsgedrag in actie te demonstreren, denk ik dat een horde zombies de perfecte pasvorm heeft. Hieronder is een demo met verschillende zombies (met verschillende snelheden) op zoek naar de muiscursor. Kunst van SpicyPixel en Clint Bellanger, van OpenGameArt.


Verplaats de muiscursor. Klik om krachten te tonen.

Conclusie

Het botsingsvermijdingsgedrag laat elk personage obstakels in de omgeving ontwijken. Aangezien alle stuurkrachten elke game-update opnieuw berekenen, werken de personages naadloos op verschillende obstakels in, waarbij altijd de meest bedreigende (het dichtst) wordt geanalyseerd.

Hoewel dit gedrag geen padzoekalgoritme is, zijn de behaalde resultaten tamelijk overtuigend voor overvolle kaarten.