Înțelegerea comportamentelor de direcție Urmați calea

Urmărirea traseului este o problemă frecventă în dezvoltarea jocurilor. Acest tutorial acoperă următorul pas comportament de direcție, care permite caracterelor să urmeze o cale predefinită formată din puncte și linii.

Notă: Deși acest tutorial este scris folosind AS3 și Flash, ar trebui să puteți utiliza aceleași tehnici și concepte în aproape orice mediu de dezvoltare a jocului. Trebuie să aveți o înțelegere de bază a vectorilor de matematică.


Introducere

O cale care urmează comportamentul poate fi implementată în mai multe moduri. Implementarea originală Reynolds folosește o cale formată din linii, în care personajele le respectă strict, aproape ca un tren pe șine.

În funcție de situație, este posibil ca această precizie să nu fie necesară. Un personaj poate să se deplaseze de-a lungul unei căi care urmează liniile, dar să le folosească ca a referinţă, mai degrabă decât ca șine.

Implementarea următorului comportament în acest tutorial este o simplificare a celei originale propusă de Reynolds. Încă mai produce rezultate bune, dar nu se bazează pe calcule grele de matematică, cum ar fi proiecții vectoriale.


Definirea unei căi

O cale poate fi definită ca un set de puncte (noduri) conectate prin linii. Chiar dacă curbele pot fi de asemenea folosite pentru a descrie o cale, punctele și liniile sunt mai ușor de manevrat și produc aproape aceleași rezultate.

Dacă trebuie să utilizați curbe, acestea pot fi reduse la un set de puncte conectate:


Curbe și linii.

Clasa cale va fi folosit pentru a descrie ruta. Practic, clasa are un vector de puncte și câteva metode pentru a gestiona lista respectivă:

 calea de clasă publică privat var nodes: Vector.; funcția publică funcțională () this.nodes = new Vector.();  funcția publică addNode (nod: Vector3D): void nodes.push (node);  funcția publică getNodes (): Vector. noduri returnate; 

Fiecare punct din cale este a Vector3D reprezentând o poziție în spațiu, la fel ca personajul poziţie lucrări de proprietate.


Trecerea de la nod la nod

Pentru a naviga pe calea gândită, caracterul se va muta de la nod la nod până când ajunge la sfârșitul traseului.

Fiecare punct al căii poate fi văzut ca o țintă, astfel încât comportamentul căutării poate fi folosit:

Căutați un punct după altul.

Persoana va căuta punctul curent până când este atins, atunci următorul punct din cale devine cel curent și așa mai departe. După cum sa descris anterior în tutorialul de evitare a coliziunilor, forțele fiecărui comportament sunt recalculate fiecare actualizare de joc, astfel încât tranziția de la un nod la altul este neîntreruptă și netedă.

Clasa caracterului va avea nevoie de două proprietăți suplimentare pentru a instrumenta procesul de navigare: nodul curent (cel care caută caracterul) și o referință la calea urmată. Clasa va arata dupa cum urmeaza:

 clasa publică Boid public path path: Cale; public var actualNode: int; (...) calea funcției privateFollowing (): Vector3D var target: Vector3D = null; dacă (cale! = null) var nodes: Vector. = path.getNodes (); target = noduri [currentNode]; dacă (distanță (poziție, țintă) <= 10)  currentNode += 1; if (currentNode >= nodes.length); currentNode = nodes.length - 1;  întoarce null;  distanța funcției private (a: Obiect, b: Obiect): Număr retur Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));  (...)

pathFollowing () metoda este cea responsabilă de generarea traseului după forță. În prezent nu produce nici o forță, dar selectează obiectivele în mod corespunzător.

 calea! = null test verifică dacă caracterul urmărește orice cale. În cazul în care este cazul currentNode proprietatea este utilizată pentru a căuta ținta curentă (cea pe care personajul trebuie să o caute) în lista de puncte.

Dacă distanța dintre ținta curentă și poziția caracterului este mai mică decât 10, înseamnă că personajul a atins nodul curent. Dacă se întâmplă asta, currentNode este incrementată de una, adică personajul va căuta punctul următor în cale. Procesul se repetă până când traseul nu se termină.


Calculul și adăugarea forțelor

Forța folosită pentru a împinge caracterul spre fiecare nod din cale este forța de căutare. pathFollowing () metoda deja alege nodul corespunzător, deci acum trebuie să returneze o forță care va împinge caracterul spre acel nod:

 calea funcției privateFollowing (): Vector3D var target: Vector3D = null; dacă (cale! = null) var nodes: Vector. = path.getNodes (); target = noduri [currentNode]; dacă (distanță (poziție, țintă) <= 10)  currentNode += 1; if (currentNode >= nodes.length); currentNode = nodes.length - 1;  retur țintă! = null? căutați (țintă): Vector3D () nou; 

După ce se calculează traseul după forța, trebuie adăugat la vectorul vitezei caracterului ca de obicei:

 direcție = nimic (); // vectorul nul, adică "magnitudinea forței zero" direcție = direcție + cale Următoare (); direcție = trunchi (direcție, max_force) direcție = direcție / viteză masă = trunchiat (viteză + direcție, max_speed) poziție = poziție + viteză

Calea urmată de forța de direcție este extrem de similară cu comportamentul de urmărire, unde caracterul ajustează în mod constant direcția sa pentru a prinde ținta. Diferența constă în modul în care personajul caută o țintă imobiliară, care este ignorată în favoarea altei, de îndată ce personajul devine prea apropiat.

Rezultatul este următorul:

Urmați calea în acțiune. Faceți clic pentru a afișa forțele.

Netezirea Mișcării

Actuala implementare necesită ca toate caracterele să "atingă" punctul curent din cale pentru a selecta următoarea țintă. Ca o consecință, un personaj ar putea efectua modele nedorite de mișcare, cum ar fi deplasarea în cercuri în jurul unui punct până la atingerea acestuia.

În natură, fiecare mișcare tinde să se supună principiului celui mai mic efort. De exemplu, o persoană nu va merge tot timpul în mijlocul unui coridor; dacă există o întoarcere, persoana va merge cu atenție spre pereți în timp ce se rotește pentru a scurta distanța.

Acest model poate fi recreat prin adăugarea unei raze la cale. Raza este aplicată punctelor și poate fi văzută ca fiind "lățimea" traseului. Acesta va controla cât de îndepărtat un personaj se poate muta de la punctele de-a lungul drumului:

Influența razei pe următoarea cale.

Dacă distanța dintre caracter și punct este mai mică sau egală cu raza, punctul este considerat a fi atins. În consecință, toate caracterele se vor mișca utilizând liniile și punctele ca ghiduri:

Urmați calea cu raza. Faceți clic pe butonul "Force" pentru a afișa forțele. Faceți clic pe butoanele "+" și "-" pentru a regla dinamic dimensiunea razei.

Cu cât raza este mai mare, cu atât este mai mare traseul și cu atât mai mare este distanța pe care personajele o va păstra de la punctele de întoarcere. Valoarea razei poate fi modificată pentru a produce diferite modele.


Mergând înapoi și în cele din urmă

Uneori este util ca un personaj să rămână în mișcare după ce ajunge la sfârșitul căii. Într-un model de patrulare, de exemplu, caracterul ar trebui să revină la începutul rutei după ce ajunge la sfârșit, urmând aceleași puncte.

Acest lucru poate fi obținut prin adăugarea pathDir proprietate la clasa personajului; acesta este un număr întreg care controlează direcția în care caracterul se mișcă de-a lungul căii. Dacă pathDir este 1, înseamnă că personajul se îndreaptă spre sfârșitul căii; -1 înseamnă o mișcare spre început.

pathFollowing () metoda poate fi modificată la:

 calea funcției privateFollowing (): Vector3D var target: Vector3D = null; dacă (cale! = null) var nodes: Vector. = path.getNodes (); target = noduri [currentNode]; dacă (distanță (poziție, țintă) <= path.radius)  currentNode += pathDir; if (currentNode >= nodes.length || currentNode < 0)  pathDir *= -1; currentNode += pathDir;    return target != null ? seek(target) : new Vector3D(); 

Spre deosebire de versiunea mai veche, valoarea lui pathDir este acum adăugată la proprietate currentNode (în loc să adăugați pur și simplu 1). Aceasta permite caracterului să selecteze următorul punct al căii bazat pe direcția curentă.

După aceasta, un test verifică dacă caracterul a ajuns la sfârșitul traseului. Dacă așa stau lucrurile, pathDir este înmulțită cu -1, care inversează valoarea sa, făcând caracterul să inverseze și direcția de mișcare.

Rezultatul este un model de mișcare înapoi și înapoi:

Cale urmând cu raza și modelul din spate și înapoi. Faceți clic pe butonul "Force" pentru a afișa forțele. Faceți clic pe butoanele "+" și "-" pentru a regla dinamic dimensiunea razei.

Concluzie

Comportamentul următor al traseului permite oricărui personaj să se deplaseze de-a lungul unei căi predefinite. Traseul este ghidat de puncte și poate fi reglat pentru a fi mai larg sau mai îngust, producând modele de mișcare care se simt mai naturale.

Implementarea acoperită în acest tutorial este o simplificare a traseului original urmând comportamentul propus de Reynolds, dar produce în continuare rezultate convingătoare și atrăgătoare.