Fizica platformei de bază 2D, Partea 5 Detectarea coliziunii obiect vs. coliziune obiect

În această parte a seriei, vom începe să lucrăm spre a face posibil ca obiectele să nu interacționeze fizic numai cu tilemap, ci și cu orice alt obiect, prin implementarea unui mecanism de detectare a coliziunii între obiectele jocului.

Demo

Demo-ul arată rezultatul final al acestui tutorial. Utilizați WASD pentru a muta caracterul. Butonul mijlociu al mouse-ului creează o platformă unidirecțională, butonul din dreapta al mouse-ului creează o țiglă solidă, iar bara spațială creează o clonă de caractere. Glisoarele modifică dimensiunea personajului jucătorului. Obiectele care detectează o coliziune sunt semi-transparente. 

Demo-ul a fost publicat sub unitatea 5.4.0f3, iar codul sursă este, de asemenea, compatibil cu această versiune de Unity.

Detectarea coliziunii

Înainte de a vorbi despre orice fel de reacție de coliziune, cum ar fi imposibilitatea ca obiectele să treacă unul peste celălalt, trebuie mai întâi să știm dacă aceste obiecte particulare se suprapun. 

Aceasta ar putea fi o operație foarte costisitoare dacă pur și simplu am verificat fiecare obiect împotriva oricărui alt obiect din joc, în funcție de numărul de obiecte active pe care jocul trebuie să le gestioneze în prezent. Deci, pentru a ameliora puțin procesorul slab al jucătorilor noștri, vom folosi ...

Partiționarea spațială!

Acest lucru este, în principiu, împărțirea spațiului jocului în zone mai mici, ceea ce ne permite să verificăm coliziunile dintre obiectele aparținând numai aceleiași zone. Această optimizare este extrem de necesară în jocuri ca Terraria, unde lumea și numărul de obiecte care se ciocnesc sunt uriașe și obiectele sunt plasate slab. În cazul jocurilor cu ecran unic, în care numărul de obiecte este foarte restricționat de dimensiunea ecranului, acesta nu este adesea necesar, dar este totuși util.

Metoda

Cea mai populară metodă de partiționare spațială pentru spațiul 2D este arborele quad; puteți găsi descrierea acestuia în acest tutorial. Pentru jocurile mele, folosesc o structură plană, care în principiu înseamnă că spațiul de joc este împărțit în dreptunghiuri de o anumită dimensiune și verific pentru coliziuni cu obiecte care locuiesc în același spațiu dreptunghiular.

Există o nuanță pentru acest lucru: un obiect poate locui în mai multe sub-spații la un moment dat. Asta e perfect - înseamnă doar că trebuie să detectăm obiecte aparținând oricarei partiții pe care fostul nostru obiect se suprapune cu.

Datele pentru partiționare

Baza este simplă. Trebuie să știm cât de mare ar trebui să fie fiecare celulă și o matrice bidimensională, din care fiecare element este o listă de obiecte care locuiesc într-o anumită zonă. Trebuie să plasăm aceste date în clasa Map.

public int mGridAreaWidth = 16; public int mGridAreaHeight = 16; Lista publică[,] mObjectsInArea;

În cazul nostru, am decis să exprimă mărimea partiției în gresie, deci fiecare partiție este de 16 cu 16 plăci mari. 

Pentru obiectele noastre, vom dori o listă a domeniilor pe care obiectul în prezent se suprapune, precum și indicele în fiecare partiție. Să le adăugăm la MovingObject clasă.

Lista publică mAreas = Listă nouă(); Lista publică mIdsInAreas = Listă nouă();

În loc de două liste, am putea folosi un singur dicționar, dar, din nefericire, performanța generală a utilizării containerelor complexe în actuala iterație a Unity lasă mult să fie dorită, așa că vom rămâne pe lista pentru demo.

Inițializați partițiile

Să mergem mai departe pentru a calcula câte partiții avem nevoie pentru a acoperi întreaga zonă a hărții. Presupunerea este că nici un obiect nu poate pluti în afara limitelor hărții. 

mHorizontalAreasCount = Mathf.CeilToInt ((float) mWidth / (float) mGridAreaWidth); mVerticalAreasCount = Mathf.CeilToInt ((float) mHeight / (float) mGridAreaHeight);

Desigur, în funcție de dimensiunea hărții, partițiile nu trebuie să se potrivească exact limitelor de hartă. De aceea folosim un plafon de valoare calculată pentru a ne asigura că avem cel puțin suficient pentru a acoperi întreaga hartă.

Să inițiem partițiile acum.

mObjectsInArea = Listă nouă[mHorizontalAreasCount, mVerticalAreasCount]; pentru (var y = 0; y < mVerticalAreasCount; ++y)  for (var x = 0; x < mHorizontalAreasCount; ++x) mObjectsInArea[x, y] = new List(); 

Nimic nu se întâmplă aici - ne asigurăm că fiecare celulă are o listă de obiecte gata pentru a ne opera.

Atribuiți partițiile obiectului

Acum este momentul să faceți o funcție care să actualizeze zonele în care se suprapune un anumit obiect.

public void UpdateAreas (MovingObject obj) 

În primul rând, trebuie să știm cu ce plăci de hartă obiectul se suprapune. Din moment ce folosim doar AABB-uri, tot ce trebuie să verificăm este ceea ce țiglă în fiecare colț al AABB aterizează.

var topLeLeft = GetMapTileAtPoint (obj.mAABB.center + new Vector2 (-obj.mAABB.HalfSize.x, obj.mAABB.HalfSizeY)); var topRight = GetMapTileAtPoint (obj.mAABB.center + obj.mAABB.HalfSize); var bottomLeft = GetMapTileAtPoint (obj.mAABB.center - obj.mAABB.HalfSize); var bottomRight = nou Vector2i ();

Acum, pentru a obține coordonatele în spațiul partiționat, tot ce trebuie să facem este să împărțim poziția plăcilor cu dimensiunea partiției. Nu este nevoie să calculam partiția din colțul din dreapta jos chiar acum, deoarece coordonatele sale x vor fi egale cu colțul din dreapta sus și coordonatele y vor fi egale cu cele din stânga jos.

topLeft.x / = mGridAreaWidth; topLeft.y / = mGridAreaHeight; topRight.x / = mGridAreaWidth; topRight.y / = mGridAreaHeight; bottomLeft.x / = mGridAreaWidth; bottomLeft.y / = mGridAreaHeight; bottomRight.x = topRight.x; bottomRight.y = bottomLeft.y;

Toate acestea ar trebui să funcționeze pe baza presupunerii că niciun obiect nu va fi mutat în afara limitelor hărții. În caz contrar, ar trebui să avem o verificare suplimentară aici pentru a ignora obiectele care nu sunt limitate. 

Acum, este posibil ca obiectul să se găsească în întregime într-o singură partiție, poate să locuiască în două sau să poată ocupa spațiul exact în cazul în care se întâlnesc patru partiții. Aceasta presupune că nici un obiect nu este mai mare decât dimensiunea partiției, caz în care ar putea ocupa întreaga hartă și toate partițiile dacă ar fi suficient de mare! Am lucrat cu această ipoteză, așa că vom proceda astfel în tutorial. Modificările pentru a permite obiecte mai mari sunt destul de banale, totuși, așa că le voi explica și ele.

Să începem prin verificarea în care zone se suprapune caracterul. Dacă toate coordonatele partiției colțului sunt aceleași, atunci obiectul ocupă doar o singură zonă.

dacă (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); 

Dacă nu este cazul și coordonatele sunt aceleași pe axa x, atunci obiectul se suprapune cu două partiții diferite vertical.

dacă (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft);  altfel dacă (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); 

Dacă sprijinim obiecte care sunt mai mari decât partițiile, ar fi de ajuns dacă am adăugat pur și simplu toate partițiile din colțul din stânga sus la cel din stânga jos folosind o buclă.

Aceeași logică se aplică dacă numai coordonatele verticale sunt aceleași.

dacă (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft);  altfel dacă (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft);  altfel dacă (topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (topRight); 

În cele din urmă, dacă toate coordonatele sunt diferite, trebuie să adăugăm toate cele patru zone.

dacă (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft);  altfel dacă (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft);  altfel dacă (topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (topRight);  altceva mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); mOverlappingAreas.Add (topRight); mOverlappingAreas.Add (bottomRight); 

Înainte de a merge mai departe cu această funcție, trebuie să putem adăuga și elimina obiectul dintr-o anumită partiție. Să creăm aceste funcții, începând cu adăugarea.

public void AddObjectToArea (vector2i areaIndex, MovingObject obj) var zona = mObjectsInArea [areaIndex.x, areaIndex.y]; // salvați indexul obiectului din zona obj.mAreas.Add (areaIndex); obj.mIdsInAreas.Add (area.Count); // adăugați obiectul în zona zonei.Adăugați (obj); 

După cum puteți vedea, procedura este foarte simplă - adăugăm indexul zonei în lista de zone suprapuse a obiectului, adăugăm indicele corespunzător la lista de ID-uri a obiectului și adăugăm în final obiectul în partiție.

Acum, să creăm funcția de eliminare.

public void RemoveObjectFromArea (zona Vector2i areaIndex, int objIndexInArea, MovingObject obj) 

După cum puteți vedea, vom folosi coordonatele zonei în care caracterul nu se mai suprapune, indexul său în lista de obiecte din acea zonă și referința la obiectul pe care trebuie să îl eliminăm.

Pentru a elimina obiectul, îl vom schimba cu ultimul obiect din listă. Acest lucru va necesita, de asemenea, să ne asigurăm că indexul obiectului pentru această zonă este actualizat la cel pe care îl avea obiectul eliminat. Dacă nu am schimba obiectul, ar trebui să actualizăm indexurile tuturor obiectelor care merg după cea pe care trebuie să o eliminăm. În schimb, trebuie să actualizăm doar cea pe care am schimbat-o. 

Având un dicționar aici ar salva o mulțime de hassle, dar eliminarea obiectului dintr-o zonă este o operație care este necesară mult mai puțin frecvent decât iterarea prin dicționar, ceea ce trebuie făcut în fiecare cadru pentru fiecare obiect atunci când actualizăm suprapunerea obiectului zone.

// schimbați ultimul element cu cel pe care îl îndepărtăm var tmp = zona [area.Count - 1]; zona [zona.Count - 1] = obj; zona [objIndexInArea] = tmp;

Acum trebuie să găsim zona în care ne ocupăm în lista de domenii a obiectului schimbat și să schimbăm indexul din lista ID-urilor în indexul obiectului eliminat.

var tmpIds = tmp.mIdsInAreas; var tmpAreas = tmp.mAreas; pentru (int i = 0; i < tmpAreas.Count; ++i)  if (tmpAreas[i] == areaIndex)  tmpIds[i] = objIndexInArea; break;  

În cele din urmă, putem elimina ultimul obiect din partiție, care este acum o referință la obiectul pe care trebuie să-l eliminăm.

zona.RemoveAt (zona.Count - 1);

Întreaga funcție ar trebui să arate astfel:

public void RemoveObjectFromArea (Vector2i zonaIndex, int objIndexInArea, MovingObject obj) var zona = mObjectsInArea [areaIndex.x, areaIndex.y]; // schimbați ultimul element cu cel pe care îl îndepărtăm var tmp = zona [area.Count - 1]; zona [zona.Count - 1] = obj; zona [objIndexInArea] = tmp; var tmpIds = tmp.mIdsInAreas; var tmpAreas = tmp.mAreas; pentru (int i = 0; i < tmpAreas.Count; ++i)  if (tmpAreas[i] == areaIndex)  tmpIds[i] = objIndexInArea; break;   //remove the last item area.RemoveAt(area.Count - 1); 

Să revenim la funcția UpdateAreas.

Știm care sunt zonele în care caracterul se suprapune peste acest cadru, dar ultimul cadru a obiectului deja ar fi putut fi atribuit acelorași sau diferite zone. Mai întâi, să buclem prin zonele vechi și, dacă obiectul nu se mai suprapune cu ele, atunci să scoatem obiectul din acestea.

var areas = obj.mAreas; var ids = obj.mIdsInAreas; pentru (int i = 0; i < areas.Count; ++i)  if (!mOverlappingAreas.Contains(areas[i]))  RemoveObjectFromArea(areas[i], ids[i], obj); //object no longer has an index in the area areas.RemoveAt(i); ids.RemoveAt(i); --i;  

Acum să buclem prin noi zone și, dacă obiectul nu a fost anterior atribuit lor, să le adăugăm acum.

pentru (var i = 0; i < mOverlappingAreas.Count; ++i)  var area = mOverlappingAreas[i]; if (!areas.Contains(area)) AddObjectToArea(area, obj); 

În cele din urmă, eliminați lista de zone suprapuse, astfel încât să fie gata să proceseze următorul obiect.

mOverlappingAreas.Clear ();

Asta e! Funcția finală ar trebui să arate astfel:

public void UpdateAreas (MovingObject obj) // obțineți zonele din colțurile lui aabb var topLeft = GetMapTileAtPoint (obj.mAABB.center + new Vector2 (-obj.mAABB.HalfSize.x, obj.mAABB.HalfSizeY)); var topRight = GetMapTileAtPoint (obj.mAABB.center + obj.mAABB.HalfSize); var bottomLeft = GetMapTileAtPoint (obj.mAABB.center - obj.mAABB.HalfSize); var bottomRight = nou Vector2i (); topLeft.x / = mGridAreaWidth; topLeft.y / = mGridAreaHeight; topRight.x / = mGridAreaWidth; topRight.y / = mGridAreaHeight; bottomLeft.x / = mGridAreaWidth; bottomLeft.y / = mGridAreaHeight; bottomRight.x = topRight.x; bottomRight.y = bottomLeft.y; // a se vedea câte domenii diferite avem (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft);  altfel dacă (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft);  altfel dacă (topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (topRight);  altceva mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); mOverlappingAreas.Add (topRight); mOverlappingAreas.Add (bottomRight);  var areas = obj.mAreas; var ids = obj.mIdsInAreas; pentru (int i = 0; i < areas.Count; ++i)  if (!mOverlappingAreas.Contains(areas[i]))  RemoveObjectFromArea(areas[i], ids[i], obj); //object no longer has an index in the area areas.RemoveAt(i); ids.RemoveAt(i); --i;   for (var i = 0; i < mOverlappingAreas.Count; ++i)  var area = mOverlappingAreas[i]; if (!areas.Contains(area)) AddObjectToArea(area, obj);  mOverlappingAreas.Clear(); 

Detectați coliziunea între obiecte

Mai întâi de toate, trebuie să ne asigurăm că sunăm UpdateAreas pe toate obiectele de joc. Putem face acest lucru în buclă de actualizare principală, după fiecare apel de actualizare a fiecărui obiect individual.

void FixedUpdate () pentru (int i = 0; i < mObjects.Count; ++i)  switch (mObjects[i].mType)  case ObjectType.Player: case ObjectType.NPC: ((Character)mObjects[i]).CustomUpdate(); mMap.UpdateAreas(mObjects[i]); break;   

Înainte de a crea o funcție în care verificăm toate coliziunile, să creăm un struct care să dețină datele coliziunii. 

Acest lucru va fi foarte util, deoarece vom putea păstra datele așa cum este în momentul coliziunii, în timp ce dacă salvăm numai referința la un obiect cu care ne-am ciocnit, nu numai că nu ar trebui să avem prea puține lucruri pentru a lucra, dar și poziția și alte variabile s-ar fi putut modifica pentru acel obiect înainte de momentul în care efectiv ajungem la procesul de coliziune în bucla de actualizare a obiectului.

(vector2), vector2 (= vector2), vector2 (= vector2), vector2 (= Vector2), vector2 (2) Vector2 pos1 = implicit (Vector2), Vector2 pos2 = implicit (Vector2)) this.other = alt; this.overlap = suprapunere; this.speed1 = speed1; this.speed2 = speed2; this.oldPos1 = oldPos1; this.oldPos2 = oldPos2; this.pos1 = pos1; this.pos2 = pos2;  public MovingObject alt; suprapunerea publicului public; public Vector2 viteză1, viteză2; publicul Vector2 oldPos1, oldPos2, pos1, pos2; 

Datele pe care le salvăm este referința la obiectul cu care ne-am ciocnit, suprapunerea, viteza ambelor obiecte în momentul coliziunii, pozițiile lor și pozițiile lor chiar înainte de momentul coliziunii.

Să trecem la MovingObject și creați un container pentru datele de coliziune proaspăt create, pe care trebuie să le detectăm.

Lista publică mAllCollidingObjects = Listă nouă();

Acum să ne întoarcem la Hartă clasa și a crea un CheckCollisions funcţie. Aceasta va fi funcția noastră greu de lucru în cazul în care vom detecta coliziuni între toate obiectele de joc.

public void CheckCollisions () 

Pentru a detecta coliziunile, vom fi iterați prin toate partițiile.

pentru (int y = 0; y < mVerticalAreasCount; ++y)  for (int x = 0; x < mHorizontalAreasCount; ++x)  var objectsInArea = mObjectsInArea[x, y];  

Pentru fiecare partiție, vom fi iterați prin fiecare obiect din cadrul ei.

pentru (int y = 0; y < mVerticalAreasCount; ++y)  for (int x = 0; x < mHorizontalAreasCount; ++x)  var objectsInArea = mObjectsInArea[x, y]; for (int i = 0; i < objectsInArea.Count - 1; ++i)  var obj1 = objectsInArea[i];   

Pentru fiecare obiect, verificăm fiecare obiect care se află în jos în lista din partiție. În acest fel, vom verifica fiecare coliziune o singură dată.

pentru (int y = 0; y < mVerticalAreasCount; ++y)  for (int x = 0; x < mHorizontalAreasCount; ++x)  var objectsInArea = mObjectsInArea[x, y]; for (int i = 0; i < objectsInArea.Count - 1; ++i)  var obj1 = objectsInArea[i]; for (int j = i + 1; j < objectsInArea.Count; ++j)  var obj2 = objectsInArea[j];    

Acum putem verifica dacă AABB-urile obiectelor se suprapun între ele.

Vector2 se suprapun; pentru (int y = 0; y < mVerticalAreasCount; ++y)  for (int x = 0; x < mHorizontalAreasCount; ++x)  var objectsInArea = mObjectsInArea[x, y]; for (int i = 0; i < objectsInArea.Count - 1; ++i)  var obj1 = objectsInArea[i]; for (int j = i + 1; j < objectsInArea.Count; ++j)  var obj2 = objectsInArea[j]; if (obj1.mAABB.OverlapsSigned(obj2.mAABB, out overlap))      

Iată ce se întâmplă în AABB OverlapsSigned funcţie.

boolee publice OverlapsSigned (alt AABB, suprapunere Vector2) suprapunere = Vector2.zero; dacă HalfSizeX == 0.0f || HalfSizeY == 0.0f || other.HalfSizeX == 0.0f || other.HalfSizeY == 0.0f || Mathf.Abs (center.x - other.center.x)> HalfSizeX + alte.HalfSizeX || Mathf.Abs (center.y - other.center.y)> HalfSizeY + other.HalfSizeY) return false; suprapus = nou Vector2 (Mathf.Sign (center.x - other.center.x) * ((alt.HalfSizeX + HalfSizeX)) - Mathf.Abs (center.x - other.center.x)), Mathf.Sign .y - other.center.y) * ((altele.HalfSizeY + HalfSizeY) - Mathf.Abs (center.y - other.center.y))); return true; 

După cum puteți vedea, dacă mărimea unei AABB pe orice axă este zero, nu se poate colizi cu ea. Celălalt lucru pe care l-ați putea observa este că, chiar dacă suprapunerea este egală cu zero, funcția va reveni la adevărat, deoarece va respinge cazurile în care decalajul dintre AABB este mai mare decât zero. Acest lucru se datorează în principal faptului că, dacă obiectele se ating unul de celălalt și nu se suprapun, totuși dorim să avem informațiile că acesta este cazul, așa că trebuie să trecem prin. 

Ca ultimul lucru, după detectarea coliziunii, calculăm cât se suprapune AABB cu celălalt AABB. Suprapunerea este semnată, deci în acest caz dacă AABB suprapus se află pe partea dreaptă AABB, suprapunerea pe axa x va fi negativă și dacă celălalt AABB se află pe partea stângă a lui AABB, suprapunerea pe axa x va fi pozitiv. Acest lucru va face mai ușor mai târziu să ieșiți din poziția suprapusă, deoarece știm în ce direcție vrem să se miște obiectul.

Mutarea la noi CheckCollisions Dacă nu există suprapunere, putem trece la următorul obiect, dar dacă s-a produs o suprapunere, atunci trebuie să adăugăm datele de coliziune la ambele obiecte.

dacă (obj1.mAABB.OverlapsSigned (obj2.mAABB, se suprapune)) obj1.mAllCollidingObjects.Add (nou CollisionData (obj2, overlap, obj1.mSpeed, obj2.mSpeed, obj1.mOldPosition, obj2.mOldPosition, obj1.mPosition, obj2.mPosition)); obj2.mAllCollidingObjects.Add (noul CollisionData (obj1, -overlap, obj2.mSpeed, obj1.mSpeed, obj2.mOldPosition, obj1.mOldPosition, obj2.mPosition, obj1.mPozi)); 

Pentru a ne ușura lucrurile, vom presupune că structura 1 (viteza1, pos1, oldPos1) din structura CollisionData se referă întotdeauna la proprietarul datelor de coliziune, iar cele două sunt datele referitoare la celălalt obiect. 

Celălalt lucru este că suprapunerea este calculată din perspectiva lui obj1. Oblicarea lui obj2 trebuie negată, deci dacă obj1 trebuie să se deplaseze la stânga pentru a ieși din coliziune, obj2 va trebui să se deplaseze dreapta pentru a ieși din aceeași coliziune.

Există încă un lucru mic pe care trebuie să-l îngrijiți - pentru că noi iterăm prin partițiile hărții și un obiect poate fi în mai multe partiții la fel, până la patru în cazul nostru, este posibil să detectăm o suprapunere pentru aceleași două obiecte de până la patru ori. 

Pentru a elimina această posibilitate, pur și simplu verificăm dacă am detectat deja o coliziune între două obiecte. Dacă este cazul, depășim repetarea.

dacă (obj1.mAABB.OverlapsSigned (obj2.mAABB, out overlap) &&! obj1.HasCollisionDataFor (obj2)) obj1.mAllCollidingObjects.Add (noul CollisionData (obj2, overlap, obj1.mSpeed, obj2.mSpeed, obj1.mOldPosition, obj2.mOldPosition, obj1.mPoziție, obj2.mPoziție)); obj2.mAllCollidingObjects.Add (noul CollisionData (obj1, -overlap, obj2.mSpeed, obj1.mSpeed, obj2.mOldPosition, obj1.mOldPosition, obj2.mPosition, obj1.mPozi)); 

HasCollisionDataFor funcția este implementată după cum urmează.

public boot HasCollisionDataFor (altă MovingObject) pentru (int i = 0; i < mAllCollidingObjects.Count; ++i)  if (mAllCollidingObjects[i].other == other) return true;  return false; 

Pur și simplu iterează prin toate structurile de date de coliziune și caută dacă aparțin deja obiectului pe care suntem pe punctul de a verifica coliziunea pentru. 

Acest lucru ar trebui să fie bine în caz de utilizare generală, deoarece nu ne așteptăm ca un obiect să se ciocnească cu multe alte obiecte, așa că privirile prin listă vor fi rapide. Cu toate acestea, într - un scenariu diferit ar fi mai bine să înlocuiți lista de CollisionData cu un dicționar, așa că, în loc de iterare, am putea spune imediat dacă un element este deja în sau nu. 

Celălalt lucru este că această verificare ne salvează să adăugăm mai multe copii ale aceleiași coliziuni la aceeași listă, dar dacă obiectele nu se ciocnesc, vom verifica oricum suprapunerea de mai multe ori dacă ambele obiecte aparțin acelorași partiții. 

Acest lucru nu ar trebui să fie o preocupare mare, deoarece verificarea coliziunii este ieftină și situația nu este atât de comună, dar dacă ar fi o problemă, soluția ar putea fi pur și simplu să aibă o matrice de coliziuni verificate sau un dicționar în ambele sensuri după cum se verifică coliziunile și reinițializați-o chiar înainte de a apela CheckCollisions funcţie.

Acum să numim funcția pe care tocmai am terminat-o în buclă principală de joc.

void FixedUpdate () pentru (int i = 0; i < mObjects.Count; ++i)  switch (mObjects[i].mType)  case ObjectType.Player: case ObjectType.NPC: ((Character)mObjects[i]).CustomUpdate(); mMap.UpdateAreas(mObjects[i]); mObjects[i].mAllCollidingObjects.Clear(); break;   mMap.CheckCollisions(); 

Asta e! Acum, toate obiectele noastre ar trebui să aibă datele despre coliziuni.

Pentru a testa dacă totul funcționează corect, hai să facem ca, dacă un personaj se ciocnește cu un obiect, sprite-ul caracterului devine semi-transparent.

După cum puteți vedea, detectarea pare să funcționeze bine!

rezumat

Asta e pentru o altă parte a seriei simple de fizică a platformerului 2D. Am reușit să implementăm un mecanism de partiționare spațial foarte simplu și să detectăm coliziunile dintre fiecare obiect. 

Dacă aveți o întrebare, un sfat despre cum să faceți ceva mai bun sau dacă aveți doar o opinie despre tutorial, nu ezitați să utilizați secțiunea de comentarii pentru a vă anunța!