In questa parte della serie, inizieremo a lavorare per rendere possibile agli oggetti non solo interagire fisicamente con la tilemap da soli, ma anche con qualsiasi altro oggetto, implementando un meccanismo di rilevamento delle collisioni tra gli oggetti del gioco.
La demo mostra il risultato finale di questo tutorial. Usa WASD per spostare il personaggio. Il pulsante centrale del mouse genera una piattaforma unidirezionale, il pulsante destro del mouse genera una tessera solida e la barra spaziatrice genera un clone di carattere. I cursori cambiano le dimensioni del personaggio del giocatore. Gli oggetti che rilevano una collisione sono resi semi-trasparenti.
La demo è stata pubblicata sotto Unity 5.4.0f3 e il codice sorgente è anche compatibile con questa versione di Unity.
Prima di parlare di qualsiasi tipo di risposta di collisione, ad esempio rendendo impossibile l'attraversamento reciproco degli oggetti, dobbiamo prima sapere se questi oggetti particolari si sovrappongono.
Questa operazione potrebbe essere molto costosa se semplicemente controllassimo ciascun oggetto rispetto a tutti gli altri oggetti del gioco, a seconda del numero di oggetti attivi che il gioco deve attualmente gestire. Quindi per alleggerire un po 'il povero processore dei nostri giocatori, useremo ...
Questo fondamentalmente sta dividendo lo spazio del gioco in aree più piccole, il che ci consente di controllare le collisioni tra oggetti appartenenti solo alla stessa area. Questa ottimizzazione è fortemente necessaria in giochi come Terraria, dove il mondo e il numero di possibili oggetti in collisione sono enormi e gli oggetti sono scarsamente posizionati. Nei giochi a schermo singolo, in cui il numero di oggetti è fortemente limitato dalle dimensioni dello schermo, spesso non è necessario, ma è comunque utile.
Il metodo di partizionamento spaziale più popolare per lo spazio 2D è il quad tree; puoi trovare la sua descrizione in questo tutorial. Per i miei giochi, sto usando una struttura piatta, che in pratica significa che lo spazio di gioco è diviso in rettangoli di una certa dimensione, e sto verificando le collisioni con oggetti che risiedono nello stesso spazio rettangolare.
C'è una sfumatura in questo: un oggetto può risiedere in più di un sub-spazio alla volta. È assolutamente perfetto, significa solo che abbiamo bisogno di rilevare oggetti appartenenti a una delle partizioni con cui il nostro oggetto precedente si sovrappone.
La base è semplice. Abbiamo bisogno di sapere quanto deve essere grande ogni cella e una matrice bidimensionale, di cui ogni elemento è una lista di oggetti che risiedono in una particolare area. Abbiamo bisogno di inserire questi dati nella classe Map.
public int mGridAreaWidth = 16; public int mGridAreaHeight = 16; lista pubblica[,] mObjectsInArea;
Nel nostro caso, ho deciso di esprimere la dimensione della partizione in tessere, e quindi ogni partizione è 16 per 16 tessere grandi.
Per i nostri oggetti, vorremmo un elenco di aree con cui l'oggetto si sta attualmente sovrapponendo, così come il suo indice in ogni partizione. Aggiungiamo questi al MovingObject
classe.
lista pubblicamAreas = nuova lista (); lista pubblica mIdsInAreas = nuova lista ();
Invece di due elenchi, potremmo utilizzare un singolo dizionario, ma sfortunatamente il sovraccarico delle prestazioni dell'utilizzo di contenitori complessi nell'iterazione corrente di Unity lascia molto a desiderare, quindi continueremo con gli elenchi per la demo.
Passiamo al calcolo del numero di partizioni di cui abbiamo bisogno per coprire l'intera area della mappa. L'assunto qui è che nessun oggetto può fluttuare fuori dai limiti della mappa.
mHorizontalAreasCount = Mathf.CeilToInt ((float) mWidth / (float) mGridAreaWidth); mVerticalAreasCount = Mathf.CeilToInt ((float) mHeight / (float) mGridAreaHeight);
Naturalmente, a seconda delle dimensioni della mappa, le partizioni non devono necessariamente corrispondere ai limiti della mappa. Ecco perché stiamo usando un massimale di valore calcolato per assicurarci di avere almeno il necessario per coprire l'intera mappa.
Iniziamo ora le partizioni.
mObjectsInArea = nuova lista[mHorizontalAreasCount, mVerticalAreasCount]; per (var y = 0; y < mVerticalAreasCount; ++y) for (var x = 0; x < mHorizontalAreasCount; ++x) mObjectsInArea[x, y] = new List ();
Non c'è niente di strano qui: ci assicuriamo solo che ogni cella abbia una lista di oggetti pronti per l'uso.
Ora è il momento di creare una funzione che aggiorni le aree che un particolare oggetto si sovrappone.
pubblico vuoto UpdateAreas (MovingObject obj)
Prima di tutto, dobbiamo sapere con quali mappe si sovrappone l'oggetto. Dal momento che stiamo usando solo gli AABB, tutto ciò che dobbiamo controllare è ciò che affianca ogni angolo dell'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 = new Vector2i ();
Ora per ottenere le coordinate nello spazio partizionato, tutto ciò che dobbiamo fare è dividere la posizione della piastrella in base alla dimensione della partizione. Non abbiamo bisogno di calcolare la partizione dell'angolo in basso a destra in questo momento, perché la sua coordinata x sarà uguale all'angolo in alto a destra, e la sua coordinata y sarà uguale a quella in basso a sinistra.
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;
Tutto questo dovrebbe funzionare sulla base dell'assunto che nessun oggetto verrà spostato al di fuori dei limiti della mappa. Altrimenti dovremmo avere un controllo aggiuntivo qui per ignorare gli oggetti che sono fuori limite.
Ora, è possibile che l'oggetto risieda interamente in una singola partizione, che possa risiedere in due o che possa occupare lo spazio proprio dove si incontrano quattro partizioni. Questo è sotto il presupposto che nessun oggetto è più grande della dimensione della partizione, nel qual caso potrebbe occupare l'intera mappa e tutte le partizioni se fosse abbastanza grande! Ho operato sotto questa ipotesi, quindi è come gestiremo questo nel tutorial. Le modifiche per consentire oggetti più grandi sono piuttosto banali, quindi, spiegherò anche loro.
Iniziamo controllando con quali aree il personaggio si sovrappone. Se tutte le coordinate della partizione dell'angolo sono uguali, l'oggetto occupa solo una singola area.
if (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft);
Se questo non è il caso e le coordinate sono le stesse sull'asse x, l'oggetto si sovrappone con due diverse partizioni verticalmente.
if (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); else if (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft);
Se stessimo supportando oggetti più grandi delle partizioni, sarebbe sufficiente aggiungere semplicemente tutte le partizioni dall'angolo in alto a sinistra a quello in basso a sinistra usando un loop.
La stessa logica si applica se solo le coordinate verticali sono le stesse.
if (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); else if (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); else if (topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (topRight);
Infine, se tutte le coordinate sono diverse, è necessario aggiungere tutte e quattro le aree.
if (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); else if (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); else if (topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (topRight); else mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); mOverlappingAreas.Add (topRight); mOverlappingAreas.Add (bottomRight);
Prima di procedere con questa funzione, dobbiamo essere in grado di aggiungere e rimuovere l'oggetto da una partizione particolare. Creiamo queste funzioni, iniziando con l'aggiunta.
pubblico vuoto AddObjectToArea (Vector2i areaIndex, MovingObject obj) var area = mObjectsInArea [areaIndex.x, areaIndex.y]; // salva l'indice dell'oggetto nella zona obj.mAreas.Add (areaIndex); obj.mIdsInAreas.Add (area.Count); // aggiungi l'oggetto all'area area. Aggiungi (obj);
Come potete vedere, la procedura è molto semplice: aggiungiamo l'indice dell'area all'elenco delle aree sovrapposte dell'oggetto, aggiungiamo l'indice corrispondente all'elenco degli oggetti dell'oggetto e infine aggiungiamo l'oggetto alla partizione.
Ora creiamo la funzione di rimozione.
pubblico vuoto RemoveObjectFromArea (Vector2i areaIndex, int objIndexInArea, MovingObject obj)
Come puoi vedere, utilizzeremo le coordinate dell'area in cui il personaggio non si sovrappone più, il suo indice nell'elenco degli oggetti all'interno di quell'area e il riferimento all'oggetto che dobbiamo rimuovere.
Per rimuovere l'oggetto, lo scambiamo con l'ultimo oggetto nell'elenco. Questo ci imporrà anche di assicurarci che l'indice dell'oggetto per questa particolare area venga aggiornato a quello che aveva il nostro oggetto rimosso. Se non avessimo scambiato l'oggetto, avremmo bisogno di aggiornare gli indici di tutti gli oggetti che seguono quello che dobbiamo rimuovere. Invece, dobbiamo aggiornare solo quello con cui ci siamo scambiati.
Avere un dizionario qui risparmierebbe un sacco di problemi, ma rimuovere l'oggetto da un'area è un'operazione che è necessaria molto meno frequentemente rispetto a iterare attraverso il dizionario, il che deve essere fatto ogni fotogramma per ogni oggetto quando stiamo aggiornando la sovrapposizione dell'oggetto le zone.
// scambia l'ultimo elemento con quello che stiamo rimuovendo var tmp = area [area.Count - 1]; area [area.Count - 1] = obj; area [objIndexInArea] = tmp;
Ora dobbiamo trovare l'area che ci interessa nella lista delle aree dell'oggetto scambiato e cambiare l'indice nella lista ids nell'indice dell'oggetto rimosso.
var tmpIds = tmp.mIdsInAreas; var tmpAreas = tmp.mAreas; per (int i = 0; i < tmpAreas.Count; ++i) if (tmpAreas[i] == areaIndex) tmpIds[i] = objIndexInArea; break;
Infine, possiamo rimuovere l'ultimo oggetto dalla partizione, che ora è un riferimento all'oggetto che dovevamo rimuovere.
area.RemoveAt (area.Count - 1);
L'intera funzione dovrebbe assomigliare a questa:
pubblico vuoto RemoveObjectFromArea (Vector2i areaIndex, int objIndexInArea, MovingObject obj) var area = mObjectsInArea [areaIndex.x, areaIndex.y]; // scambia l'ultimo elemento con quello che stiamo rimuovendo var tmp = area [area.Count - 1]; area [area.Count - 1] = obj; area [objIndexInArea] = tmp; var tmpIds = tmp.mIdsInAreas; var tmpAreas = tmp.mAreas; per (int i = 0; i < tmpAreas.Count; ++i) if (tmpAreas[i] == areaIndex) tmpIds[i] = objIndexInArea; break; //remove the last item area.RemoveAt(area.Count - 1);
Torniamo alla funzione UpdateAreas.
Sappiamo quali aree il carattere si sovrappone a questo fotogramma, ma l'ultimo fotogramma che l'oggetto avrebbe potuto già essere assegnato alle stesse o aree diverse. Per prima cosa, passiamo in rassegna le vecchie aree e, se l'oggetto non si sovrappone più a loro, rimuoviamo l'oggetto da queste.
var areas = obj.mAreas; var ids = obj.mIdsInAreas; per (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;
Ora passiamo in rassegna le nuove aree e se l'oggetto non è stato precedentemente assegnato a loro, aggiungiamole ora.
per (var i = 0; i < mOverlappingAreas.Count; ++i) var area = mOverlappingAreas[i]; if (!areas.Contains(area)) AddObjectToArea(area, obj);
Infine, cancella l'elenco delle aree sovrapposte in modo che sia pronto per elaborare l'oggetto successivo.
mOverlappingAreas.Clear ();
Questo è tutto! La funzione finale dovrebbe assomigliare a questa:
pubblico vuoto UpdateAreas (MovingObject obj) // richiama le aree agli angoli degli 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 = new 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; // guarda quante diverse aree abbiamo se (topLeft.x == topRight.x && topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); else if (topLeft.x == topRight.x) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); else if (topLeft.y == bottomLeft.y) mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (topRight); else mOverlappingAreas.Add (topLeft); mOverlappingAreas.Add (bottomLeft); mOverlappingAreas.Add (topRight); mOverlappingAreas.Add (bottomRight); var areas = obj.mAreas; var ids = obj.mIdsInAreas; per (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();
Prima di tutto, dobbiamo assicurarci di chiamare UpdateAreas
su tutti gli oggetti del gioco. Possiamo farlo nel ciclo di aggiornamento principale, dopo ogni chiamata di aggiornamento di ciascun oggetto.
void FixedUpdate () for (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;
Prima di creare una funzione in cui controlliamo tutte le collisioni, creiamo una struttura che trattiene i dati della collisione.
Questo sarà molto utile, perché saremo in grado di conservare i dati così come sono al momento della collisione, mentre se salvassimo solo il riferimento a un oggetto con cui siamo entrati in collisione, non solo avremmo troppo poco per lavorare, ma anche la posizione e altre variabili potrebbero essere cambiate per quell'oggetto prima del tempo in cui arriviamo ad elaborare la collisione nel ciclo di aggiornamento dell'oggetto.
public struct CollisionData public CollisionData (MovingObject altro, Vector2 overlap = default (Vector2), Vector2 speed1 = default (Vector2), Vector2 speed2 = default (Vector2), Vector2 oldPos1 = default (Vector2), Vector2 oldPos2 = default (Vector2), Vector2 pos1 = default (Vector2), Vector2 pos2 = default (Vector2)) this.other = other; this.overlap = sovrapposizione; this.speed1 = speed1; this.speed2 = speed2; this.oldPos1 = oldPos1; this.oldPos2 = oldPos2; this.pos1 = pos1; this.pos2 = pos2; Altro pubblico di MovingObject; sovrapposizione pubblica di Vector2; pubblico Vector2 speed1, speed2; pubblico Vector2 oldPos1, oldPos2, pos1, pos2;
I dati che salviamo sono il riferimento all'oggetto con cui siamo entrati in collisione, la sovrapposizione, la velocità di entrambi gli oggetti al momento della collisione, le loro posizioni e anche le loro posizioni poco prima del momento di collisione.
Passiamo al MovingObject
classe e creare un contenitore per i dati di collisione appena creati che dobbiamo rilevare.
lista pubblicamAllCollidingObjects = new List ();
Ora torniamo al Carta geografica
classe e creare a CheckCollisions
funzione. Questa sarà la nostra funzione pesante dove rileviamo le collisioni tra tutti gli oggetti del gioco.
public void CheckCollisions ()
Per rilevare le collisioni, continueremo a scorrere tutte le partizioni.
per (int y = 0; y < mVerticalAreasCount; ++y) for (int x = 0; x < mHorizontalAreasCount; ++x) var objectsInArea = mObjectsInArea[x, y];
Per ogni partizione, continueremo a scorrere ogni oggetto al suo interno.
per (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];
Per ogni oggetto, controlliamo ogni altro oggetto che si trova più in basso nella lista nella partizione. In questo modo controlleremo ogni collisione solo una volta.
per (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];
Ora possiamo verificare se gli AABB degli oggetti si sovrappongono l'un l'altro.
Vector2 si sovrappongono; per (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))
Ecco cosa succede negli AABB OverlapsSigned
funzione.
public bool OverlapsSigned (AABB other, out Vector2 overlap) overlap = Vector2.zero; if (HalfSizeX == 0.0f || HalfSizeY == 0.0f || altro.HalfSizeX == 0.0f || altro.HalfSizeY == 0.0f || Mathf.Abs (center.x - other.center.x)> HalfSizeX + other.HalfSizeX || Mathf.Abs (center.y - other.center.y)> HalfSizeY + other.HalfSizeY) restituisce false; overlap = new Vector2 (Mathf.Sign (center.x - other.center.x) * ((altro.HalfSizeX + HalfSizeX) - Mathf.Abs (center.x - other.center.x)), Mathf.Sign (centro .y - other.center.y) * ((other.HalfSizeY + HalfSizeY) - Mathf.Abs (center.y - other.center.y))); ritorna vero;
Come puoi vedere, se le dimensioni di un AABB su qualsiasi asse sono pari a zero, non possono essere scontrate. L'altra cosa che si può notare è che anche se la sovrapposizione è uguale a zero, la funzione restituirà true, in quanto rifiuterà i casi in cui il divario tra gli AABB è maggiore di zero. Questo è principalmente dovuto al fatto che se gli oggetti si toccano l'un l'altro e non si sovrappongono, vogliamo comunque avere l'informazione che questo è il caso, quindi abbiamo bisogno di questo per passare attraverso.
Come ultima cosa, una volta rilevata la collisione, calcoliamo quanto l'AABB si sovrappone all'altra AABB. La sovrapposizione è firmata, quindi in questo caso se l'AABB sovrapposto si trova sul lato destro di questo AABB, la sovrapposizione sull'asse x sarà negativa, e se l'altro AABB si trova sul lato sinistro di questo AABB, la sovrapposizione sull'asse x sarà positivo. Ciò renderà più facile in seguito uscire dalla posizione di sovrapposizione, poiché sappiamo in quale direzione vogliamo che l'oggetto si muova.
Tornando al nostro CheckCollisions
funzione, se non ci fosse sovrapposizione, il gioco è fatto, possiamo passare all'oggetto successivo, ma se si verifica una sovrapposizione, dobbiamo aggiungere i dati di collisione a entrambi gli oggetti.
if (obj1.mAABB.OverlapsSigned (obj2.mAABB, out overlap)) obj1.mAllCollidingObjects.Add (new CollisionData (obj2, overlap, obj1.mSpeed, obj2.mSpeed, obj1.mOldPosition, obj2.mOldPosition, obj1.mPosition, obj2.mPosition)); obj2.mAllCollidingObjects.Add (new CollisionData (obj1, -overlap, obj2.mSpeed, obj1.mSpeed, obj2.mOldPosition, obj1.mOldPosition, obj2.mPosition, obj1.mPosition));
Per semplificarci, supponiamo che gli 1 (speed1, pos1, oldPos1) nella struttura CollisionData facciano sempre riferimento al proprietario dei dati di collisione e che i 2 siano i dati relativi all'altro oggetto.
L'altra cosa è che la sovrapposizione è calcolata dalla prospettiva dell'obj1. La sovrapposizione dell'obj2 deve essere negata, quindi se obj1 deve spostarsi a sinistra per uscire dalla collisione, obj2 dovrà spostarsi a destra per uscire dalla stessa collisione.
C'è ancora una piccola cosa da tenere in considerazione, perché stiamo ripetendo le partizioni della mappa e un oggetto può trovarsi in più partizioni allo stesso, fino a quattro nel nostro caso, è possibile che rileveremo una sovrapposizione per lo stesso due oggetti fino a quattro volte.
Per rimuovere questa possibilità, controlliamo semplicemente se abbiamo già rilevato una collisione tra due oggetti. Se è così, saltiamo l'iterazione.
if (obj1.mAABB.OverlapsSigned (obj2.mAABB, out overlap) &&! obj1.HasCollisionDataFor (obj2)) obj1.mAllCollidingObjects.Add (new CollisionData (obj2, overlap, obj1.mSpeed, obj2.mSpeed, obj1.mOldPosition, obj2.mOldPosition, obj1.mPosition, obj2.mPosition)); obj2.mAllCollidingObjects.Add (new CollisionData (obj1, -overlap, obj2.mSpeed, obj1.mSpeed, obj2.mOldPosition, obj1.mOldPosition, obj2.mPosition, obj1.mPosition));
Il HasCollisionDataFor
la funzione è implementata come segue.
public bool HasCollisionDataFor (MovingObject other) for (int i = 0; i < mAllCollidingObjects.Count; ++i) if (mAllCollidingObjects[i].other == other) return true; return false;
Semplicemente itera su tutte le strutture dei dati di collisione e cerca se qualcuno già appartiene all'oggetto che stiamo per controllare per la collisione.
Questo dovrebbe andar bene in caso di uso generale, poiché non ci aspettiamo che un oggetto si scontrino con molti altri oggetti, quindi scorrere l'elenco sarà rapido. Tuttavia, in uno scenario diverso potrebbe essere preferibile sostituire l'elenco di CollisionData
con un dizionario, così invece di iterare potremmo dire subito se un elemento è già presente o meno.
L'altra cosa è che questo controllo ci salva dall'aggiungere più copie della stessa collisione allo stesso elenco, ma se gli oggetti non sono in collisione, controlleremo comunque la sovrapposizione più volte se entrambi gli oggetti appartengono alle stesse partizioni.
Questo non dovrebbe essere un grosso problema, dato che il controllo delle collisioni è economico e la situazione non è comune, ma se fosse un problema, la soluzione potrebbe essere semplicemente avere una matrice di collisioni controllate o un dizionario bidirezionale, riempire fino a quando le collisioni vengono controllate, e resettate a destra prima di chiamare il CheckCollisions
funzione.
Chiamiamo ora la funzione che abbiamo appena terminato nel ciclo di gioco principale.
void FixedUpdate () for (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();
Questo è tutto! Ora tutti i nostri oggetti dovrebbero avere i dati sulle collisioni.
Per verificare se tutto funziona correttamente, facciamo in modo che se un personaggio si scontra con un oggetto, lo sprite del personaggio diventa semitrasparente.
Come puoi vedere, il rilevamento sembra funzionare bene!
Questo è tutto per un'altra parte della semplice serie di fisica del platform 2D. Siamo riusciti a implementare un meccanismo di partizionamento spaziale molto semplice e rilevare le collisioni tra ciascun oggetto.
Se hai una domanda, un consiglio su come fare qualcosa di meglio, o hai solo un'opinione sul tutorial, sentiti libero di usare la sezione commenti per farmi sapere!