2009-05-22 13 views
14

Sto sviluppando un gioco che presenta una notevole area di gioco piazza 2d. L'area di gioco è priva di spigoli con lati delimitati (non avvolgenti). Sto cercando di capire come posso meglio dividere questo mondo per aumentare le prestazioni del rilevamento delle collisioni. Piuttosto che controllare che ogni entità sia in collisione con tutte le altre entità, voglio controllare solo le entità vicine per evitare collisioni e ostacoli.Come suddividere un mondo di gioco 2d per il rilevamento delle collisioni meglio

Ho alcuni problemi particolari in questo mondo di gioco ...

  • voglio essere in grado di essere in grado di utilizzare un gran numero di soggetti nel mondo di gioco in una sola volta. Tuttavia, una% di entità non entrerà in collisione con entità dello stesso tipo. Ad esempio i proiettili non entrano in collisione con altri proiettili.

  • Desidero poter utilizzare una vasta gamma di dimensioni di entità. Voglio che ci sia una differenza di dimensioni molto grandi tra le entità più piccole e le più grandi.

  • Ci sono poche entità statiche o non in movimento nel mondo di gioco.

Mi interessa utilizzando qualcosa di simile a ciò che è descritto nella risposta qui: Quadtree vs Red-Black tree for a game in C++?

La mia preoccupazione è quanto bene sarà un albero di suddivisione del mondo in grado di gestire le differenze di grandi dimensioni in entità? Per dividere il mondo abbastanza per le entità più piccole, quelle più grandi dovranno occupare un gran numero di regioni e sono preoccupato di come ciò influenzerà le prestazioni del sistema.

altra mia preoccupazione principale è come mantenere correttamente l'elenco delle aree occupate fino ad oggi. Poiché ci sono molte entità in movimento, e alcune molto grandi, sembra che la divisione del mondo creerà una quantità significativa di spese generali per tenere traccia di quali entità occupano le regioni.

Sono principalmente alla ricerca di algoritmi o idee validi che consentano di ridurre il numero di rilevamenti di collisioni e di evitare gli ostacoli.

risposta

2

Ci sono un sacco di approcci. Suggerirei impostazioni ad alcuni obiettivi specifici (ad esempio, x test di collisione al secondo con un rapporto di y tra le entità più piccole a quelle più grandi) e fare alcuni prototipi per trovare l'approccio più semplice che raggiunga tali obiettivi. Potresti essere sorpreso di quanto poco lavoro devi fare per ottenere ciò di cui hai bisogno. (Oppure potrebbe essere un sacco di lavoro, a seconda dei particolari.)

Molte strutture di accelerazione (ad es. Un buon BSP) possono richiedere un po 'di tempo per essere configurate e quindi sono generalmente inappropriate per l'animazione rapida.

C'è molta letteratura là fuori su questo argomento, quindi dedica un po 'di tempo alla ricerca e alla ricerca per trovare un elenco di possibili candidati. Prendili e profili.

6

Se fossi in te comincerei fuori implementando un (binario partizione di spazio) semplice albero BSP. Dato che stai lavorando in 2D, i controlli bound box sono molto veloci.È fondamentalmente bisogno di tre classi: CBspTree, CBspNode e CBspCut (non proprio necessaria)

  1. CBspTree ha un'istanza nodo principale della classe CBspNode
  2. CBspNode ha un'istanza di CBspCut
  3. CBspCut simboleggiare come si taglia un set in due set disgiunti. Questo può essere risolto con precisione introducendo il polimorfismo (ad esempio CBspCutX o CBspCutY o qualche altra linea di taglio). CBspCut ha anche due CBspNode

L'interfaccia verso il mondo diviso sarà attraverso la classe albero e può essere una buona idea per creare un altro strato in cima a quello, nel caso in cui si vorrebbe sostituire il BSP soluzione con es un albero quadruplo. Una volta che ci stai imparando. Ma nella mia esperienza, un BSP andrà benissimo.

Ci sono diverse strategie su come memorizzare i tuoi oggetti nell'albero. Ciò che intendo è che puoi scegliere di avere ad es. una sorta di contenitore in ogni nodo che contiene riferimenti agli oggetti che occupano quell'area. Ciò significa però (come ti stai chiedendo) che gli oggetti di grandi dimensioni occuperanno molte foglie, cioè ci saranno molti riferimenti a oggetti di grandi dimensioni e oggetti molto piccoli verranno mostrati alle foglie singole.

Nella mia esperienza questo non ha un impatto così grande. Certo che conta, ma dovresti fare dei test per verificare se è davvero un problema o meno. Sareste in grado di aggirare questo lasciando semplicemente quegli elementi ai nodi ramificati nell'albero, cioè non li memorizzerete su "livello foglia". Ciò significa che troverai quegli oggetti veloci mentre attraversi l'albero.

Quando si tratta della prima domanda. Se si utilizzerà questa suddivisione per i test di collisione e nient'altro, suggerisco che le cose che non possono mai scontrarsi non vengano mai inserite nell'albero. Un missile, ad esempio, come dici tu, non può scontrarsi con un altro missile. Il che significherebbe che non devi nemmeno immagazzinare il missile nell'albero.

Tuttavia, si potrebbe voler usare il bsp anche per altre cose, non lo si è specificato, ma tenetelo a mente (per il prelievo di oggetti con, ad esempio, il mouse). Altrimenti, propongo di archiviare tutto nel bsp e di risolvere la collisione in seguito. Basta chiedere al bsp di un elenco di oggetti in una certa area per ottenere un insieme limitato di possibili candidati alla collisione ed eseguire il controllo dopo di ciò (supponendo che gli oggetti sappiano con cosa possono scontrarsi, o qualche altro meccanismo esterno).

Se si vuole accelerare le cose, è necessario anche prendersi cura di merge e dividere, vale a dire quando le cose vengono rimossi dall'albero, un sacco di nodi diventerà vuoto o il numero di elementi di seguito alcune il livello del nodo diminuirà al di sotto di qualche soglia di unione. Quindi si desidera unire due sottoalberi in un nodo contenente tutti gli elementi. La divisione avviene quando si inseriscono oggetti nel mondo. Quindi, quando il numero di elementi supera la soglia di splitting, introduci un nuovo taglio, che divide il mondo in due. Queste soglie di unione e divisione dovrebbero essere due costanti che è possibile utilizzare per ottimizzare l'efficienza dell'albero.

L'unione e la divisione vengono utilizzate principalmente per mantenere l'albero bilanciato e per assicurarsi che funzioni il più efficientemente possibile in base alle sue specifiche. Questo è davvero ciò di cui hai bisogno di preoccuparti. Spostare le cose da una posizione e quindi aggiornare l'albero è imo veloce. Ma quando si tratta di unire e dividere, potrebbe diventare costoso se lo si fa troppo spesso.

Questo può essere evitato introducendo una sorta di lazy merge e split system, cioè si ha qualche tipo di flag sporco o conteggio delle modifiche. Raccogli tutte le operazioni che possono essere dosate, ovvero spostare 10 oggetti e inserire 5 potrebbe essere un lotto.Una volta terminato il gruppo di operazioni, si controlla se l'albero è sporco e quindi si esegue l'operazione di unione e/o divisione.

Invia alcuni commenti se vuoi che spieghi ulteriormente.

Cheers!


Modifica

ci sono molte cose che possono essere ottimizzate nell'albero. Ma come sai, l'ottimizzazione prematura è la radice di tutto il male. Quindi inizia semplice. Ad esempio, è possibile creare un sistema di callback generico che è possibile utilizzare mentre si attraversa l'albero. In questo modo non devi eseguire una query sull'albero per ottenere un elenco di oggetti che corrispondono alla "domanda" della casella associata, ma puoi semplicemente attraversare l'albero ed eseguire quella richiamata ogni volta che colpisci qualcosa. "Se questa casella limite sto fornendo interseca voi, quindi eseguire questo callback con questi parametri"

4

La mia preoccupazione è quanto bene sarà un albero suddivisione del mondo in grado di gestire le differenze di grandi dimensioni in entità ? Per dividere il mondo su abbastanza per le entità più piccole le quelle più grandi avranno bisogno di occupare un gran numero di regioni e io sono preoccupato di come questo influenzerà le prestazioni del sistema.

Utilizzare un albero quad. Per gli oggetti che esistono in più aree di avere un paio di opzioni:

  • Conservare l'oggetto in entrambi i rami, fino in fondo. Tutto finisce nei nodi foglia, ma si può finire con un numero significativo di puntatori extra. Potrebbe essere appropriato per le cose statiche.

  • Dividere l'oggetto sul bordo della zona e inserire ciascuna parte nelle rispettive posizioni. Crea molto dolore e non è ben definito per molti oggetti.

  • Memorizza l'oggetto nel punto più basso dell'albero possibile. Gli insiemi di oggetti ora esistono nei nodi foglia e non foglia, ma ogni oggetto ha un puntatore ad esso nell'albero. Probabilmente il migliore per oggetti che si stanno muovendo.

Tra l'altro, il motivo per cui stai usando un albero quad è perché è davvero molto facile da lavorare. Non hai alcuna creazione basata euristica come potresti con alcune implementazioni di BSP. È semplice e fa il lavoro.

altra mia grande preoccupazione è come mantenere correttamente l'elenco delle occupate aree fino ad oggi. Dal momento che c'è un sacco di entità in movimento, e alcuni molto grandi , sembra dividere il mondo up creerà un significativo quantità di overhead per tenere traccia di quali entità occupano quali regioni.

Ci sarà un sovraccarico per mantenere le tue entità nei punti corretti nell'albero ogni volta che si spostano, sì, e può essere significativo. Ma il punto è che stai facendo molto meno lavoro nel tuo codice di collisione.Anche se stai aggiungendo un po 'di overhead con l'attraversamento dell'albero e l'aggiornamento dovrebbe essere molto più piccolo del sovraccarico che hai appena rimosso usando l'albero.

Ovviamente a seconda del numero di oggetti, della dimensione del mondo di gioco, ecc. Ecc., Il trade off potrebbe non valerne la pena. Di solito risulta essere una vittoria, ma è difficile saperlo senza farlo.

1

Sarei tentato solo di sovrapporre una griglia grossolana sull'area di gioco per formare un hash 2D. Se la griglia è almeno la dimensione della più grande entità, allora hai sempre solo 9 caselle di griglia per verificare le collisioni ed è molto più semplice della gestione di alberi quadrangolari o alberi BSP arbitrari. L'overhead di determinare quale griglia quadrata grossolana si trova in è in genere solo 2 operazioni aritmetiche e quando viene rilevato un cambiamento la griglia deve solo rimuovere un riferimento/ID/puntatore da un quadrato e aggiungere lo stesso ad un altro quadrato.

Si possono ottenere ulteriori guadagni dal tenere i proiettili fuori dal sistema di ricerca griglia/albero/ecc. Dal momento che è possibile determinare rapidamente dove si troverà il proiettile nella griglia, si conoscono i quadrati della griglia per interrogare potenziali collisioni. Se controlli a turno le collisioni contro l'ambiente per ciascun proiettile, non è necessario che le altre entità controllino le collisioni contro i proiettili al contrario.

Problemi correlati