Förstå styrningsbeteenden sökväg följande

Steg som följer är ett vanligt problem i spelutveckling. Denna handledning täcker sökväg följer styrningsbeteende, vilket gör det möjligt för tecken att följa en fördefinierad bana av punkter och linjer.

Notera: Även om denna handledning skrivs med AS3 och Flash, borde du kunna använda samma tekniker och begrepp i nästan vilken spelutvecklingsmiljö som helst. Du måste ha en grundläggande förståelse för matematiska vektorer.


Introduktion

En väg som följer beteende kan implementeras på flera sätt. Det ursprungliga Reynolds-genomförandet använder en linje i linjer, där karaktärerna följer dem strikt, nästan som ett tåg på rails.

Beroende på situationen kan sådan precision inte vara nödvändig. Ett tecken kan röra sig längs en bana som följer rader, men använder dem som en referens, snarare än som skenor.

Genomförandet av banans beteende i denna handledning är en förenkling av Reynolds föreslagna original. Det ger fortfarande goda resultat, men det är inte beroende av tunga matematiska beräkningar som vektorprojektioner.


Definiera en väg

En väg kan definieras som en uppsättning punkter (noder) kopplade med linjer. Även om kurvor också kan användas för att beskriva en väg, är punkter och linjer enklare att hantera och ger nästan samma resultat.

Om du behöver använda kurvor kan de minskas till en uppsättning anslutna punkter:


Kurvor och linjer.

Klassen Väg kommer att användas för att beskriva rutten. I grunden har klassen en vektor av punkter och några metoder för att hantera den listan:

 Public Class Path private var noder: Vector.; Public Function Path () this.nodes = ny vektor.();  allmän funktion addNode (nod: Vector3D): void nodes.push (node);  offentlig funktion getNodes (): Vector. returnoder; 

Varje punkt i vägen är a Vector3D representerar en position i rymden, på samma sätt som karaktären placera fastighetsarbeten.


Flytta från nod till nod

För att navigera tänk på banan flyttas karaktären från nod till nod tills den når slutet av rutten.

Varje punkt i vägen kan ses som ett mål, så sökbeteendet kan användas:

Sök en punkt efter en annan.

Tecknet kommer att söka den aktuella punkten tills den nås, då blir nästa punkt i sökvägen den nuvarande och så vidare. Som tidigare beskrivits i kollisionsundviklingsövningen beräknas varje beteendes styrkor varje speluppdatering, så övergången från en nod till en annan är sömlös och jämn.

Teckenets klass behöver två ytterligare egenskaper för att instrumentet ska navigera processen: den nuvarande noden (den som karaktären söker) och en hänvisning till den sökväg som följs. Klassen kommer att se ut som följer:

 offentliga klassen Boid public var path: Path; public var currentNode: int; (...) privata funktionen pathFollowing (): Vector3D var mål: Vector3D = null; om (väg! = null) var noder: Vector. = path.getNodes (); target = noder [currentNode]; om (avstånd (position, mål) <= 10)  currentNode += 1; if (currentNode >= noder. längd) currentNode = noder. längd - 1;  returnera null;  privata funktionsavstånd (a: Objekt, b: Objekt): Nummer returnera Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));  (...)

De pathFollowing () Metoden är den som ansvarar för att generera vägen som följer på kraften. För närvarande producerar det ingen kraft, men det väljer målen ordentligt.

De väg! = null Testa kontroller om tecknet följer någon väg. Om så är fallet currentNode egendom används för att leta upp det aktuella målet (det som tecknet måste söka) i listan med punkter.

Om avståndet mellan det aktuella målet och karaktärens position är mindre än 10, det betyder att tecknet har nått den nuvarande noden. Om det händer, currentNode ökar med en, vilket betyder att tecknet kommer att söka nästa punkt i banan. Processen upprepas tills banan löper ut ur punkter.


Beräkna och lägga till krafter

Kraften som används för att driva karaktären mot varje nod i banan är sökkraften. De pathFollowing () Metoden väljer redan lämplig nod, så nu behöver man återställa en kraft som kommer att driva karaktären mot den noden:

 privat funktionsbanaFöljande (): Vector3D var mål: Vector3D = null; om (väg! = null) var noder: Vector. = path.getNodes (); target = noder [currentNode]; om (avstånd (position, mål) <= 10)  currentNode += 1; if (currentNode >= noder. längd) currentNode = noder. längd - 1;  returmål! = null? söka (mål): Ny Vector3D (); 

Efter det att vägen efter kraften har beräknats måste den läggas till karaktärens hastighetsvektor som vanligt:

 styrning = inget (); // null-vektorn, som betyder "nollstyrka" styrning = styrning + pathFollowing (); styrning = stympa (styrning, max_force) styrning = styrning / masshastighet = trunkera (hastighet + styrning, max_hastighet) position = läge + hastighet

Stigen som följer styrkraften är extremt lik strävan, där karaktären ständigt anpassar sin riktning för att fånga målet. Skillnaden ligger i hur karaktären söker ett obevekligt mål som ignoreras till förmån för en annan så snart karaktären blir för nära.

Resultatet är följande:

Banan följer i åtgärd. Klicka för att visa styrkor.

Utjämning av rörelsen

Den nuvarande implementeringen kräver att alla tecken ska "röra" den aktuella punkten i sökvägen för att välja nästa mål. Som ett resultat kan ett tecken utföra oönskade rörelsemönster, till exempel att flytta i cirklar runt en punkt tills den nås.

I naturen tenderar varje rörelse att lyda principen om minst ansträngning. Till exempel kommer en person inte att gå i mitten av en korridor hela tiden; Om det är en tur, går personen nära till väggarna medan han vrids för att förkorta avståndet.

Det mönstret kan återskapas genom att lägga till en radie på banan. Radien appliceras på punkterna och den kan ses som rutten "bredd". Det kommer att styra hur långt ett tecken kan röra sig från punkterna längs vägen:

Inverkan av radie på vägen efter.

Om avståndet mellan tecknet och punkten är mindre än eller lika med radie, anses punkten nådd. Som en konsekvens kommer alla tecken att flytta med linjerna och punkter som guider:

Ban följer med radie. Klicka på "Force" knappen för att visa krafter. Klicka på knapparna "+" och "-" för att justera radiens storlek dynamiskt.

Ju större radie, desto bredare rutt och desto större avstånd kommer karaktärerna att behålla från punkterna när de vrids. Radiets värde kan tweaked för att producera olika följande mönster.


Går fram och tillbaka

Ibland är det användbart för en karaktär att fortsätta flytta när den når slutet av banan. I ett patrullmönster, till exempel, ska tecknet återvända till början av rutten efter att det når slutet, efter samma punkter.

Detta kan uppnås genom att lägga till pathDir egendom till karaktärens klass; detta är ett heltal som styr den riktning som karaktären rör sig längs vägen. Om pathDir är 1, det betyder att karaktären rör sig mot slutet av banan; -1 betecknar en rörelse mot början.

De pathFollowing () Metoden kan ändras till:

 privat funktionsbanaFöljande (): Vector3D var mål: Vector3D = null; om (väg! = null) var noder: Vector. = path.getNodes (); target = noder [currentNode]; om (avstånd (position, mål) <= path.radius)  currentNode += pathDir; if (currentNode >= noder. längd || currentNode < 0)  pathDir *= -1; currentNode += pathDir;    return target != null ? seek(target) : new Vector3D(); 

Till skillnad från den äldre versionen, värdet av pathDir läggs nu till fastigheten currentNode (istället för att helt enkelt lägga till 1). Detta gör det möjligt för karaktären att välja nästa punkt i banan baserat på den aktuella riktningen.

Därefter kontrollerar ett test om tecknet har nått slutet av rutten. Om så är fallet, pathDir multipliceras med -1, som inverts dess värde, vilket gör karaktären också invertera rörelsriktningen.

Resultatet är ett fram och tillbaka rörelse mönster:

Sti som följer med radie och fram och tillbaka mönster. Klicka på "Force" knappen för att visa krafter. Klicka på knapparna "+" och "-" för att justera radiens storlek dynamiskt.

Slutsats

Banan som följer beteendet tillåter att alla tecken flyttas längs en fördefinierad bana. Ruten styrs av punkter och den kan justeras för att vara bredare eller smalare, vilket ger rörmönster som känns mer naturliga.

Genomförandet som omfattas av denna handledning är en förenkling av den ursprungliga vägen som följer av beteende som Reynolds föreslagit, men det ger fortfarande övertygande och tilltalande resultat.