Modo corretto di gestire la distruzione delle entità di gioco


10

Immagina un mondo di gioco in cui carichi e carichi di entità vengono caricati in modo dinamico tutto il tempo, lo rappresenterei forse come un elenco di entità, ma per quanto riguarda la loro rimozione?

Mentre aggiungendo potrei respingere la nuova entità, potrei avere la necessità di rimuovere in qualsiasi punto del contenitore. Per evitare di cercare l'elemento per trovare la sua posizione per la rimozione, quali sono le mie scelte?

Pensavo di poter memorizzare l'ID entità come la sua posizione nel contenitore, creando un percorso per la rimozione diretta, ma non genererebbe una sorta di "disordine" di dipendenza reciproca?

So che il modo giusto sarebbe qualcosa come List.RemoveAt (whereToRemove); ma cosa succede se solo l'entità sa quando dovrebbe morire?

O mi manca qualcosa e un contenitore di elenchi saprebbe quando un oggetto viene distrutto e ne riduce le dimensioni?

Risposte:


10

Ecco le tue condizioni:

  • Altri oggetti possono ancora dipendere dall'entità rimossa, dopo che è stata rimossa.

  • Desideri che solo l'entità specifichi la propria rimozione.

Non puoi avere entrambi. Perché? Perché il codice a un livello superiore rispetto alla tua stessa entità (vedi esempi sotto) decide quando deve essere usata quell'entità. Di conseguenza, solo il codice a quello stesso livello può determinare se la tua entità è adatta alla rimozione o meno.

Tuttavia , ciò che può accadere è che l'entità può richiedere la propria rimozione, attivando un evento che il codice di livello superiore sta ascoltando. Il livello superiore memorizza quindi questa richiesta di rimozione in un elenco.


Esempio 1: senza eventi

Stai verificando le collisioni tra entità nel tuo mondo. Questo è gestito più in alto, di solito nel tuo ciclo di gioco principale, che controlla ogni entità l'una contro l'altra. In questo esempio in particolare, quando un'entità si scontra con un'altra, solo la logica interna di quell'entità può determinare quanto danno ha subito e se è "scaduto" o meno. Quindi seguiamo il flusso logico per le collisioni in cui hai quattro entità nel tuo mondo, A, B, C e D. A è la nostra entità di cui ci occupiamo.

Controlliamo A per collisione con B. C'è una collisione. A subisce danni al 50%.

Controlliamo A per collisione con C. C'è una collisione. A subisce danni al 50%. Poiché il danno raggiunge 0, A determina che è "morto". Si rimuove dall'elenco.

Controlliamo A per la collisione con D. Non ci sarebbe stata alcuna collisione, ma non arriverai mai così lontano: ottieni un'eccezione di runtime perché il tuo elenco di entità è stato modificato nel mezzo di un'operazione traveral.

Esempio 2: con eventi

Stessa configurazione di prima.

Controlliamo A per collisione con B. C'è una collisione. A subisce danni al 50%.

Controlliamo A per collisione con C. C'è una collisione. A subisce danni al 50%. Poiché il danno raggiunge 0, A determina che è "morto". Genera un evento al codice di gestione dell'entità per dire "Rimuovimi al più presto". Il codice di gestione delle entità esamina il riferimento dell'entità inviato come parte dell'evento e memorizza tale riferimento in un elenco di entità da rimuovere.

Controlliamo A per collisioni con D. Non ci sono collisioni e il controllo funziona bene.

Ora, alla fine dell'attuale iterazione del ciclo di gioco , scorrere l'elenco delle entità da rimuovere e rimuoverle tutte dall'elenco delle entità principali.


Puoi vedere come questo evita del tutto il problema. Non è necessario utilizzare eventi, è possibile utilizzare segnali o qualcos'altro, ma il principio è lo stesso: non rimuovere entità fino a quando non è possibile farlo in modo sicuro. Il rovescio della medaglia di questo approccio, per mantenere le cose pulite e ordinate, sta facendo lo stesso con le entità da aggiungere: assicurati di conservare i riferimenti ad esse e aggiungili solo all'inizio della prossima iterazione del ciclo di gioco.

Infine, non dimenticare di eliminare sia gli elenchi da rimuovere che quelli da aggiungere, ogni volta che li usi per eseguire aggiunte / rimozioni nell'elenco delle entità principali.

PS. Non abbiate paura di cercare nella vostra lista principale per effettuare traslochi individuali. Fa parte della gestione delle entità, e anche gli elenchi di grandi dimensioni tendono ad essere molto veloci da attraversare - dopotutto, è per questo che sono progettati.


0

Stai decisamente cercando una HashMap / HashTable . Una tabella hash è una mappa che abbina una chiave a un valore specifico. La chiave può essere qualsiasi cosa (come l'ID entità).


0

Immagino che potresti usare l' idea di smartpointer per gestire le deallocazioni per te, in tal caso non sarà necessario tenere un elenco di tutte le entità all'interno del tuo codice.

in alcuni casi è necessario un elenco per scorrere su tutti gli oggetti del gioco. questo elenco potrebbe essere semplicemente un elenco di collegamenti in cui l'inserimento e la rimozione di oggetti richiederà esattamente O (1) tempo.

anche per aumentare la tua velocità puoi usare un array statico (possibilmente un vettore). in tal caso dovrai tenere traccia di 2 elenchi collegati all'interno dello stesso vettore, uno eseguirà l'iterazione su oggetti validi e l'altro eseguirà l'iterazione su oggetti liberi. ogni volta che lo smartpointer segna un punto da eliminare, è sufficiente rimuovere quel puntatore e aggiungere il suo spazio all'elenco degli spazi liberi. e ogni volta che aggiungi qualche entità devi solo rimuovere il primo spazio libero riempirlo con il puntatore entità e quindi aggiungerlo a un elenco di oggetti valido.

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.