Modifica: il secondo tentativo di questa risposta mi sono reso conto che stavo per essere trascinato in dettagli microscopici. Proverò a semplificare qui.
Pattern osservatore / coda eventi
Quindi tra le soluzioni più dirette a questo problema è usare qualcosa come Observer o qualche tipo di coda di eventi centralizzata. Il tuo sistema di rilevamento collisioni può quindi essere chiamato immediatamente in modo indiretto come osservatore del soggetto (la tua scena di gioco) o può essere passato a una serie di eventi che sono avvenuti indirettamente / astrattamente dal sistema centrale e poi scoprire cosa è successo al scena (es: elemento inserito / rimosso). Quindi puoi rispondere aggiornando l'indice spaziale di conseguenza.
Tuttavia, nel corso degli anni ho sviluppato un strong disgusto per questi tipi di soluzioni in grandi progetti perché, nel caso sfortunato in cui devi eseguire il debug di qualcosa che va storto durante una notifica di evento / osservatore, trovo che possa trasformarsi in uno scenario da incubo in cui mi sento come se fossi rimbalzato in giro come se fossi in un flipper su tutto il codice base cercando di capire cosa è andato storto.
Naturalmente uso ancora queste soluzioni qua e là, ma cerco di non appoggiarle troppo. In particolare, cerco di evitare uno scenario ricorsivo in cui un evento emette uno o più eventi aggiuntivi, o in cui un osservatore viene avvisato solo di fare qualcosa che fa sì che un altro soggetto (o peggio, lo stesso) invii più osservatori.
"Diffs"
Quindi l'alternativa principale che preferisco al giorno d'oggi è, invece di una di queste soluzioni, utilizzare una sorta di struttura dati che ti permette di trovare come "changeset" o "delta / diff" di sorta con il senno di poi.
La struttura dei dati è abbastanza leggera da copiare e tiene traccia delle informazioni necessarie in modo da poter confrontare una nuova versione di se stessa con una vecchia versione come new_version.diff(old_version)
e ti dirà tutto quello che devi sapere su cosa è successo tra il momento in cui hai archiviato la vecchia versione di esso e il momento in cui hai ricevuto quello nuovo, come quali elementi sono stati inseriti / rimossi dalla / dalla scena. Questo è un po 'vago su come si progetta questa struttura dati, ma può variare in modo selvaggio in base alle proprie esigenze, ma l'idea generale è così e la si può affrontare in molti modi.
E in effetti ho iniziato a utilizzare questa soluzione come requisito pratico. Nel mio caso non ho più un'istanza centrale di una scena. Ho tutti i tipi di thread / sistemi in esecuzione in parallelo che memorizzano la propria copia locale di una scena e generano nuove versioni modificate che poi ottengono l'output su altri sistemi (es: renderer) che quindi memorizzano la propria copia ed eseguono in parallelo e così via. Quindi nel mio caso non vi è più alcuna istanza di scena centrale che renda impraticabile qualsiasi tipo di osservatore centrale / evento in coda se non addirittura impossibile, e questo tipo di soluzione "diff" sembrava il modo più pratico per risolvere il problema. E nel mio caso quel tipo di funzionalità "diffs" è parte della scena stessa ( new_scene.diff(old_scene)
) che poi mi dice informazioni rilevanti come quali entità sono state inserite / rimosse dalla copia precedente della scena che avevo, e la scena stessa una struttura dati persistente immutabile.
Eppure, dopo averlo fatto, l'ho trovato molto più facile poiché non c'è più bisogno di avere tutta questa comunicazione tra oggetti / sistemi / thread per dirsi l'un l'altro, direttamente o indirettamente, cosa è successo allo stato precedente. I tuoi oggetti / sistemi / thread possono semplicemente guardare il nuovo stato (scena del gioco) e scoprire, a loro piacimento, nel loro thread di elaborazione, cosa è successo e apportare gli aggiornamenti necessari a qualsiasi struttura in risposta (es: indice spaziale).
In genere, ciò che sto raccomandando non è un elenco di eventi verificatisi, anche se potrebbe sembrare il modo più semplice per tenere traccia di ciò che è accaduto tra la nuova versione e il vecchio. Non è molto pratico confrontare un intero elenco crescente di cose che si sono succedute l'un l'altra per capire i cambiamenti in questo modo, e anche se si attenua l'infinito desiderio di crescita memorizzando alcuni dati aggiuntivi come un "punto cache", che sta iniziando a prendere ancora un po 'ingombrante. Quello che faccio nel mio caso è iniziare con una struttura dati a bitset che tiene traccia di quali entità occupano quali indici. Confrontando il vecchio bitset con il nuovo usando le operazioni di set rapido, vi dirò immediatamente quali entità non occupano più gli ex indici e quali entità ora occupano nuove non occupate in precedenza. Ma ovviamente questo non ti dice se qualche entità è stata rimossa e ne è stata inserita una nuova per occupare il suo precedente indice, per esempio, quindi uso anche alcuni contatori di cambiamento e cose del genere in cima alle operazioni di set per eseguire rapidamente il drill-down e scopri quali elementi sono stati rimossi / inseriti / modificati. Questo è un esempio di base e quelli della mia vita reale sono molto più sfumati, ma dovrebbero darti un inizio per quanto riguarda il modo in cui potresti implementarlo.