Quad alberi / scontro basato sulla griglia - attuare la logica


13

Prima di tutto, ho scritto la mia logica di gioco solo per un breve periodo, quindi mi scuso se potrebbe sembrare semplice.

Ho letto molto sugli alberi dei quad e sul rilevamento delle collisioni basato sulla griglia. Capisco la logica - fondamentalmente non controllare la collisione a meno che gli oggetti non siano praticamente vicini. Ma non viene mai menzionato come eseguono effettivamente questo.

Ho un paio di possibili metodi nella mia testa ma non sono sicuro di quale sia il migliore

Test di collisione generale: nessuna ottimizzazione

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }

store neighbors (metodo 1) Ma cosa succede se vogliamo ottimizzare le collisioni per controllare solo gli oggetti vicini. Eseguiamo ancora tutti gli oggetti o creiamo un array con oggetti vicini da controllare?

var objects:Array = new Array();
    var neighbours:Array = new Array();

    for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){

               neighbours.push(objectA, objectB);
              }
        }

    }

    //somewhere else
    for(i:int = 0; i < neighbours.length; i++){
        //only check neighbours

        for(j:int = i + 1; j < neighbours.length; j++){

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }
    }

esegui il ciclo di tutti gli oggetti ma controlla solo i vicini per la collisione (metodo 3) L'altra possibilità è che eseguiamo ancora il ciclo di tutto, ma controlliamo se gli oggetti sono vicini prima di testare la collisione.

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){
               //they are near - check collision!
               if(objectA.collidesWith(objectB){

               //handle collision logic
              }
            }
        }

    }

Memorizza oggetti nei dati delle tessere (metodo 3) L' uso di un sistema basato su tessere consente un'altra opzione; Conservare gli oggetti che si trovano su un riquadro specifico nei dati del riquadro stesso. Controlla per vedere su quale riquadro sono le tessere circostanti che contengono eventuali oggetti con cui potrebbe scontrarsi:

var ObjectA;

for(var i:int = 0; i < 4; i ++){
//check 4 surrounding tiles from object A

   if(Object.currentTile + surroundingTile[i] CONTAINS collidable object){
   //check collision!
    if(objectA.collidesWith(surroundingTile.object){

    //handle collision logic
     }

}
}

Cerco sempre di guardare al mondo reale come esempio. Se volessi confrontare gli articoli con lo stesso colore, sarebbe illogico controllare ogni intero articolo anche se non corrispondono al colore (Metodo 2, controlla ogni articolo). Probabilmente raccoglierei gli oggetti con lo stesso colore (oggetti vicini l'uno all'altro) e controllerei quelli (metodo 1), invece di controllare tutto.

Questo non è un confronto appropriato poiché gli articoli nel controllo delle collisioni si muovono costantemente, quindi l'ordine viene confuso. Questo mi confonde.

Sarebbe più efficiente controllare ogni articolo, eliminando così lo sforzo di continuare a generare una serie di vicini.

O è più efficace trovare vicini di casa, quindi non dover passare attraverso così tanti oggetti per controllare la collisione?

Continuare a modificare i dati su ogni riquadro sembra molto intenso, quindi non sono sicuro che sia una buona idea ..

Ho pensato a un gioco di difesa della torre in cui la torre ha bisogno di rilevare oggetti se gli oggetti si trovano nel raggio prima di sparargli. E sembra sciocco controllare tutti gli oggetti mentre a volte non ci saranno oggetti vicini.

Mi scuso per il lungo post, avendo sempre problemi a spiegarmi!


che lingua è questa? Javascript?
Kyle C,

2
ActionScript 3, ma la lingua è piuttosto irrilevante. Voglio solo scoprire qual è il modo migliore per gestire e strutturare questo :)
omgnoseat

Risposte:


8

È necessario ridurre il conteggio dei controlli di collisione effettivi. Sì, ovvio, lo so. Quindi approfondiamo questo:

Il tuo primo algoritmo di controllo delle collisioni senza alcuna ottimizzazione: quanti controlli eseguirà in una corsa? Se n è il conteggio degli oggetti, si tratterà di n * (n-1) controlli. Per molti oggetti (> 100) questo sarà terribilmente lento.

Il metodo di controllo del tuo vicino non sarà molto più veloce. Se i tuoi oggetti non sono statici e si muovono molto, dovrai creare questi elenchi di vicini per ogni oggetto ogni volta nel tuo ciclo di gioco. Questo è in realtà peggio del primo algoritmo poiché stai facendo n * (n-1) per costruire l'elenco dei vicini e quindi controllare per ogni oggetto se si scontra con uno dei suoi vicini.

Devi dividere il tuo spazio di gioco. Supponiamo che il tuo spazio di gioco sia largo 400x400 pixel. Puoi dividerlo in 4 spazi secondari di dimensioni 200x200 ciascuno e controllare per ogni oggetto a quale degli spazi secondari appartiene (un oggetto può trovarsi all'interno di più di uno spazio secondario). Quindi dovrai solo verificare se gli oggetti in ogni sottospazio si scontrano con altri oggetti nello stesso sottospazio.

Quindi il costo di runtime sarà: n per la creazione dell'elenco dei 4 spazi secondari + (n / 4) * ((n-1) / 4) che è molto meglio del precedente costo di runtime. Ciò può essere ulteriormente ridotto riducendo i sottospazi (ad es. 50x50).

Quindi il nostro algoritmo ora assomiglia a questo:

for each (object in objects)
  check into which subspace the object belongs

for each (subspace in subspaces)
  for each (object in subspace)
    check object for collision with other objects in same subspace

Questo è in qualche modo simile all'idea dei dati del riquadro. Ma i sottospazi non devono necessariamente avere le stesse dimensioni delle tessere.

Possiamo fare un ultimo passo per ottimizzare ulteriormente questo usando un quadrifoglio. Sia k il conteggio dei sottospazi. Per costruire gli elenchi degli oggetti degli spazi stiamo eseguendo k * n controlli, portando a molti controlli se il tuo mondo di gioco diventa grande.

Per ridurre questo costo usiamo un quadrifoglio. Un quadtree è un altro modo per dividere il nostro spazio di gioco. Invece di dividere il nostro spazio 400x400 in 64 sottospazi 50x50 e controllare per ogni oggetto in quale dei 64 sottospazi è attualmente, lo spazio di gioco è diviso in 4 sottospazi metà della dimensione dello spazio di gioco (200x200), che a loro volta sono divisi in sottospazi più piccoli (100x100), che a loro volta vengono nuovamente suddivisi in sottospazi 50x50. Questo è il nostro quadrifoglio.

Ora, se vogliamo sapere a quale sottospazio 50x50 appartiene un oggetto, controlliamo a quale dei sottospazi 200x200 appartiene. Quindi andiamo più in profondità di un livello nel nostro quadrifoglio e controlliamo con i 4 sottospazi 100x100 che si trovano all'interno del sottospazio 200x200 che abbiamo appena trovato. Questo si ripete fino a quando non sappiamo a quale sottospazio 50x50 appartiene l'oggetto. Quindi quanti controlli erano ora necessari? 4 per ogni livello del quadrifoglio (Ricorda che un oggetto può trovarsi sul bordo di due sottospazi). Quindi abbiamo bisogno di controlli 4 * 3 per assegnare un oggetto al sottospazio 50x50 corretto. Molto meglio di 64 assegni.

Quindi il nostro algoritmo quadtree necessita di controlli 4 * 3 * n per costruire gli elenchi del sottospazio e quindi qualcosa come controlli k * (n / k) per controllare le collisioni di ciascun oggetto.


Questo è un tendone molto chiaro e comprensibile, grazie mille! Come viene gestito su quale sottospazio è un oggetto? Il sottospazio viene gestito come un oggetto, in cui l'oggetto su di esso viene salvato come array e confrontato? O controlli semplicemente quale sottospazio è controllando la X e la Y all'interno del ciclo principale? Sembra che costruire e cambiare array tutto il tempo sia piuttosto inefficiente. Quindi voglio essere sicuro di usare il metodo appropriato :) Posso anche vedere un problema che si verifica quando gli oggetti sono di dimensioni variabili. Immagino che il sottospazio più piccolo dovrebbe essere grande quanto l'oggetto più grande?
omgnoseat

Sono felice di aiutarti :) Un sottospazio sarebbe un oggetto che mantiene i 4 sottospazi che contiene. Il sottospazio più piccolo è diverso poiché non contiene più sottospazi, ma un elenco dei tuoi oggetti. Considerando il costo di aggiornamento: l'albero del sottospazio viene creato una volta all'inizio del gioco. In ogni ciclo del ciclo principale gli elenchi di oggetti nei sottospazi più piccoli vengono cancellati e riempiti di nuovo. Quindi è necessario solo l'elenco Aggiungi / Rimuovi. Non è necessario cambiare array. Il sottospazio più piccolo dovrebbe essere abbastanza grande da contenere diversi oggetti di gioco. Quanto grande dipende dal tuo gioco e può essere modificato in seguito.
Stephen,

Grazie, ho dimenticato che controlleremo anche le collisioni tra gli oggetti reali nel sottospazio più piccolo, ha senso che ora dovrebbe essere in grado di contenere più oggetti. Ho iniziato un test e sta andando bene. Il problema maggiore che sto riscontrando è rilevare a quale sottospazio appartiene un oggetto. Continuo a scorrere ciclicamente i valori X e Y dei sottospazi, è un metodo appropriato?
omgnoseat

Come gestite gli oggetti che si estendono in più di un sottospazio (ovvero la cui posizione si trova su o vicino a un confine)?
mghicks,

Semplice: io no. Gli oggetti possono far parte di più sottospazi, se si trovano al limite di ciascun sottospazio. Quindi un oggetto può far parte di un massimo di 4 sottospazi. Ma questa è la minoranza.
Stephen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.