Regulile simple ale comportamentelor de flocking Aliniere, Coeziune și Separare

În lumea naturală, organismele manifestă anumite comportamente atunci când călătoresc în grupuri. Acest fenomen, cunoscut și ca îmbulzesc, apare atât la niveluri microscopice (bacterii) cât și la scale macroscopice (pești). Folosind computerele, aceste modele pot fi simulate prin crearea unor reguli simple și combinarea acestora. Acest lucru este cunoscut sub numele de comportament emergent, și poate fi folosit în jocuri pentru a simula o mișcare de grup haotică sau de viață.

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


Introducere

În acest tutorial, voi acoperi cele trei reguli principale utilizate pentru a simula flocul și explicați cum să implementați fiecare. Înainte de a începe, iată câteva terminologii pe care le voi folosi:

  • Agent: O singură entitate sau caracter.
  • Velocity vector: Viteza curentă a unui agent.
  • Cartier: O anumită zonă în jurul agentului, folosit pentru a căuta alți agenți.
  • rezultanta: Vectorul obținut din calculele regulii.

Această demonstrație arată efectele celor trei reguli de flocking pe care le voi explica în acest tutorial: aliniere, coeziune, și separare.

Codul sursă complet pentru această demonstrație poate fi descărcat aici, astfel încât acest articol va evidenția doar cele mai importante aspecte ale implementării. Simțiți-vă liber să descărcați sursa dacă doriți să aflați mai multe.


Aliniere


Imagine adaptată din articolul lui Craig Reynolds

Alinierea este un comportament care determină ca un anumit agent să se alinieze cu agenții apropiați.

În primul rând, vom face o funcție care ia un agent și returnează un vector de viteză.

funcția publică computeAlignment (agentul meu: agent: Point 

Vom avea nevoie de două variabile: una pentru stocarea vectorului pe care îl vom calcula și altul pentru a urmări numărul de vecini ai agentului.

var v: Punct = nou punct (); var neighborCount = 0;

Cu variabilele noastre inițializate, vom itera acum prin toți agenții și vom găsi pe cei din cadrul raza vecinului - adică acei apropiați suficient de mult pentru a fi considerați vecini ai agentului specificat. Dacă un agent este găsit în rază, viteza sa este adăugată la vectorul de calcul și numărul vecinului este incrementat.

pentru fiecare (agent var: Agent în agentArray) if (agent! = myAgent) if (agentul meuAgent.distanceFrom) < 300)  v.x += agent.velocity.x; v.y += agent.velocity.y; neighborCount++;   

Dacă nu s-au găsit vecini, vom întoarce pur și simplu vectorul zero (valoarea implicită a vectorului de calcul).

dacă (neighborCount == 0) returnați v;

În cele din urmă, divizăm vectorul de calcul de către numărul vecinului și îl normalizăm (împărțim-l cu lungimea lui pentru a obține un vector de lungime 1), obținându-se vectorul final rezultat.

v.x / = neighborCount; v.y / = neighborCount; v.normalize (1); întoarcere v;

Coeziune


Imagine adaptată din articolul lui Craig Reynolds

Coeziunea este un comportament care determină agenții să îndrepte spre "centrul de masă" - adică poziția medie a agenților într-o anumită rază.

Implementarea este aproape identică cu cea a comportamentului de aliniere, dar există unele diferențe-cheie. În primul rând, în loc să adăugați viteză la vectorul de calcul, poziţie se adaugă în schimb.

v.x + = agent.x; v.y + = agent.y;

Ca și înainte, vectorul de calcul este împărțit la numărul de vecini, rezultând poziția care corespunde centrului de masă. Cu toate acestea, nu vrem centrul de masa in sine, vrem directia către centrul de masă, astfel încât să recomputem vectorul ca distanța de la agent la centrul de masă. În cele din urmă, această valoare este normalizată și returnată.

v.x / = neighborCount; v.y / = neighborCount; v = punct nou (v.x - myAgent.x, v.y - myAgent.y); v.normalize (1); întoarcere v;

Separare


Imagine adaptată din articolul lui Craig Reynolds

Separarea este comportamentul care determină un agent să se îndepărteze de toți vecinii săi.

Punerea în aplicare a separării este foarte asemănătoare cu cea a alinierii și coeziunii, așadar voi sublinia doar ce este diferit. Când se găsește un agent învecinat, distanța de la agent la vecin este adăugată la vectorul de calcul.

v.x + = agent.x - myAgent.x; v.y + = agent.y - myAgent.y

Vectorul de calcul este împărțit la numărul de vecini corespunzători, dar înainte de normalizare există un pas mai important. Vectorul calculat trebuie să fie negat pentru ca agentul să se îndepărteze de vecinii săi în mod corespunzător.

v.x * = -1; v.y * = -1;

Punându-le pe toți împreună

Odată ce aceste trei reguli au fost puse în aplicare, trebuie să se reunească. Cea mai simplă modalitate de a face acest lucru este după cum urmează:

var aliniere = computeAlignment (agent); var cohesion = calculul coeziunii (agent); var separare = computație de separare (agent); agent.velocity.x + = aliniere.x + coeziune.x + separarex; agent.velocity.y + = aliniere.y + coeziune.y + separare.y; agent.velocity.normalize (AGENT_SPEED);

Aici, eu pur și simplu calculez cele trei reguli pentru un anumit agent și le adaug la viteza. Apoi, normalizez viteza și apoi se înmulțește cu unele constante reprezentând viteza implicită pentru un agent. Este posibil să o sporiți și mai mult adăugând greutăți pentru fiecare regulă pentru a optimiza comportamentele:

agent.velocity.x + = alignment.x * aliniereWeight + cohesion.x * coeziuneWeight + separation.x * separationWeight; agent.velocity.y + = alignment.y * alignmentWeight + cohesion.y * coeziuneWeight + separation.y * separationWeight;

Modificarea acestor greutăți va schimba modul în care se aglomentează agenții. Asigurați-vă că experimentați numerele până când găsiți ceva ce vă place.

Iată demo-ul din nou, astfel încât să puteți încerca:


Concluzie

Flocking-ul este simplu de implementat, dar are unele rezultate puternice. Dacă faceți un joc cu AI, în special grupuri mari de AI care interacționează unul cu celălalt, flocul poate fi util. Folosiți-l bine.