Ci scusiamo per la resurrezione del filo antico, ma le griglie vecchie semplici IMHO non sono usate abbastanza spesso per questi casi. Ci sono molti vantaggi in una griglia in quanto l'inserimento / rimozione delle celle è sporco a buon mercato. Non devi preoccuparti di liberare una cella poiché la griglia non ha l'obiettivo di ottimizzare le rappresentazioni sparse. Dico che dopo aver ridotto i tempi di selezione selezionare un gruppo di elementi in una base di codice legacy da oltre 1200ms fino a 20ms semplicemente sostituendo il quad-tree con una griglia. In tutta onestà, tuttavia, quel quad-albero è stato implementato in modo inadeguato, memorizzando un array dinamico separato per nodo foglia per gli elementi.
L'altro che trovo estremamente utile è che i tuoi algoritmi di rasterizzazione classici per disegnare forme possono essere utilizzati per effettuare ricerche nella griglia. Ad esempio, puoi usare la rasterizzazione della linea di Bresenham per cercare elementi che si intersecano con una linea, la rasterizzazione della linea di scansione per trovare quali celle intersecano un poligono, ecc. Dato che lavoro molto nell'elaborazione delle immagini, è davvero bello poter usare esattamente lo stesso codice ottimizzato che uso per tracciare pixel su un'immagine mentre uso per rilevare intersezioni contro oggetti in movimento in una griglia.
Detto questo, per rendere efficiente una griglia, non dovresti avere bisogno di più di 32 bit per cella di griglia. Dovresti essere in grado di memorizzare un milione di celle in meno di 4 megabyte. Ogni cella della griglia può semplicemente indicizzare il primo elemento nella cella e il primo elemento nella cella può quindi indicizzare il successivo elemento nella cella. Se stai conservando una sorta di contenitore completo con ogni singola cella, questo diventa esplosivo nell'uso della memoria e nelle allocazioni rapidamente. Invece puoi semplicemente fare:
struct Node
{
int32_t next;
...
};
struct Grid
{
vector<int32_t> cells;
vector<Node> nodes;
};
Così:
Va bene, così ai contro. Devo ammetterlo, con una certa propensione e preferenza verso le reti, ma il loro principale svantaggio è che non sono scarne.
L'accesso a una cella della griglia specifica data una coordinata è a tempo costante e non richiede la discesa di un albero che è più economico, ma la griglia è densa, non sparsa, quindi potresti finire per controllare più celle del necessario. In situazioni in cui i tuoi dati sono distribuiti in modo molto scarso, la griglia potrebbe richiedere un controllo più approfondito per capire gli elementi che si intersecano, ad esempio una linea o un poligono pieno o un rettangolo o un cerchio di delimitazione. La griglia deve archiviare quella cella a 32 bit anche se è completamente vuota e quando si esegue una query di intersezione della forma, è necessario controllare quelle celle vuote se intersecano la forma.
Il vantaggio principale del quad-tree è naturalmente la sua capacità di archiviare dati sparsi e suddividere solo quanto necessario. Detto questo, è più difficile da implementare davvero bene, soprattutto se hai cose che si muovono in ogni frame. L'albero deve suddividere e liberare i nodi figlio al volo in modo molto efficiente, altrimenti si degrada in una griglia densa che spreca un sovraccarico per memorizzare i collegamenti padre- figlio. È molto fattibile implementare un quad-tree efficiente usando tecniche molto simili a quelle che ho descritto sopra per la griglia, ma in genere sarà più dispendioso in termini di tempo. E se lo fai nel modo in cui lo faccio io nella griglia, anche questo non è necessariamente ottimale, dal momento che porterebbe a una perdita nella capacità di garantire che tutti e 4 i figli di un nodo quad-tree siano archiviati in modo contiguo.
Inoltre, sia un albero a quattro alberi che una griglia non fanno un lavoro magnifico se hai un numero di elementi di grandi dimensioni che coprono gran parte dell'intera scena, ma almeno la griglia rimane piatta e non si suddivide all'ennesima potenza in quei casi . Il quad-albero dovrebbe immagazzinare elementi tra i rami e non solo foglie per gestire ragionevolmente tali casi, altrimenti vorrà suddividersi come un matto e degradare la qualità in modo estremamente rapido. Ci sono più casi patologici come questo che devi affrontare con un albero quad se vuoi che gestisca la più ampia gamma di contenuti. Ad esempio, un altro caso che può davvero inciampare in un albero quad è se si dispone di un carico di elementi coincidenti. A quel punto alcune persone ricorrono semplicemente alla definizione di un limite di profondità per il loro albero quad per impedirne la suddivisione all'infinito. La griglia ha un fascino che fa un lavoro decente,
La stabilità e la prevedibilità sono anche vantaggiose in un contesto di gioco, poiché a volte non si desidera necessariamente la soluzione più rapida possibile per il caso comune se a volte può portare a singhiozzi nei frame rate in scenari di casi rari rispetto a una soluzione che è ragionevolmente veloce tutto- ma non porta mai a tali inconvenienti e mantiene i frame rate fluidi e prevedibili. Una griglia ha quel tipo di quest'ultima qualità.
Detto questo, penso davvero che spetti al programmatore. Con cose come grid vs. quad-tree o octree vs. kd-tree vs. BVH, il mio voto è sullo sviluppatore più prolifico con un record per la creazione di soluzioni molto efficienti, indipendentemente dalla struttura dei dati che utilizza. C'è anche molto a livello micro, come multithreading, SIMD, layout di memoria compatibili con la cache e schemi di accesso. Alcune persone potrebbero considerare quei micro ma non hanno necessariamente un micro impatto. Queste cose potrebbero fare la differenza 100 volte da una soluzione all'altra. Nonostante ciò, se mi venissero dati personalmente alcuni giorni e mi fosse stato detto che dovevo implementare una struttura di dati per accelerare rapidamente il rilevamento delle collisioni di elementi che si muovevano in ogni frame, farei meglio in quel breve tempo implementando una griglia piuttosto che un quad -albero.