Sbarazzarsi di oggetti morti in un gioco in modo efficace?


10

Sto usando un ciclo for o ciclo foreach (non importa) per scorrere tutti i miei oggetti quando devono essere aggiornati o disegnati. Tuttavia, quando un oggetto viene ucciso, voglio che venga rimosso di nuovo dalla raccolta.

Faccio questo aggiungendo l'oggetto a un elenco di oggetti morti e poi, quando tutto è stato disegnato e aggiornato, passo attraverso tutto in questo elenco, rimuovendo anche gli oggetti nell'elenco dalla fonte originale.

C'è un modo migliore per farlo?



2
Questo non ha davvero nulla a che fare con la garbage collection, nonostante l'uso della parola "garbage" nel titolo e le risposte finora che hanno citato .NET Garbage Collector.
Andrew Russell,

Mi sono preso la libertà di cambiare effettivamente la parola "immondizia" in "morto", per evitare confusione con .NET Garbage Collector.
Nevermind,

Oh, ne sono consapevole. L'articolo di Garbage Collection è ancora rilevante perché potrebbe giocare a ciò che fai con quegli oggetti morti. Ad esempio detriti morti? Scollegalo da tutto, ripristinalo ai valori predefiniti, rimettilo nel secchio dei detriti.
doppelgreener,

Risposte:


11

Innanzitutto: terminologia. Se dici "lista dei rifiuti", la gente penserà che stai parlando del Garbage Collector. Chiamiamolo la "lista morta".

Come probabilmente hai scoperto, quindi la tua domanda, non puoi rimuovere elementi da una raccolta mentre stai iterando attraverso di essa. Se la tua raccolta è un modo List<>allora il modo più semplice per rimuovere elementi da essa è:

for(int i = list.Count-1; i >= 0; --i)
{
    if(list[i].IsDead)
        list.RemoveAt(i);
}

Si noti che si esegue l'iterazione all'indietro . È possibile rimuovere gli elementi mentre si procede in avanti, ma è più caotico e richiede a goto. Non puoi farlo con foreach.

Puoi effettuare la rimozione separatamente dal tuo ciclo di aggiornamento. Oppure puoi unirlo nel tuo ciclo di aggiornamento. Fai sempre ilRemoveAt la fine del loop: dopo quel punto nel loop,list[i] non può essere considerato valido (diventa nuovamente valido alla successiva iterazione).

Si noti inoltre che ogni oggetto ha il suo IsDead flag (se si utilizza il modello di eliminazione, è possibile crearlo IsDisposed) - in realtà non è necessario mantenere un "elenco morto".

L'uso di un flag su ciascun elemento è preferibile per le prestazioni, poiché si evita di dover cercare nell'elenco per eseguire la rimozione. Ed è anche preferibile per la progettazione - significa che ogni oggetto può facilmente verificare se è morto - quindi non chiamare accidentalmente alcun metodo su di esso (se questi oggetti implementano il modello usa e getta, potrebbero lanciareObjectDisposedException in questo caso ).

Se si dispone di una raccolta diversa da a List<>, la rimozione di elementi morti da quella raccolta può comunque comportare la creazione di un elenco morto (poiché la rimozione durante l'iterazione potrebbe non essere possibile). A mio avviso, è più bello, dal punto di vista del design, creare la lista morta proprio prima che venga utilizzata, ripetendo la raccolta alla ricerca di IsDeadbandiere, piuttosto che cercare di mantenere la lista mentre gli oggetti vengono uccisi.

Una volta che hai finito con un elenco morto, dovresti Clear()farlo e poi tenerlo in giro per riutilizzarlo in seguito.


Quindi il modello usa e getta è considerato più 'efficace' allora?
Jonathan Connell,

@Jonathan: non capisco la tua domanda. Il IsDeadmodello è funzionalmente identico a IsDisposed. L'utilizzo semantico IsDeadimplica che stai mantenendo un elenco Disegna / Aggiorna di questi oggetti che rimuoverà gli oggetti morti in un secondo momento. Il modello di smaltimento non implica questo. Inoltre, il modello di smaltimento non implica che si può avere risorse non gestite detenuti da quell'oggetto (che di solito non è appropriata per gli oggetti di gioco).
Andrew Russell,

Puoi farlo con foreach usando reverse.
shinzou,

0

Il 99,9% delle volte, il Garbage Collector (GC) si prenderà cura di tutto senza problemi. Lascia che faccia il suo lavoro dopo aver cancellato / annullato quegli oggetti. C'è una latenza nella pulizia che dipende da una serie di fattori (ad esempio quanti blocchi di memoria sono stati assegnati ad esempio) ma in genere non è necessario nemmeno saperlo, figuriamoci preoccuparsene. È progettato per funzionare. Questo è uno dei motivi per cui stai usando C # e non C.

Per quanto riguarda le prestazioni di GC in generale, non ti preoccupare eccessivamente a meno che tu non sappia (attraverso la profilazione) che sta creando un collo di bottiglia - questo è solo in giochi piuttosto impegnativi, in generale. In questo caso, guarda GC.Collect () e le sue varie insidie, in modo da sapere quando è appropriato usarlo. Suggerisco di cercare su Google "force gc xna", c'è un sacco di materiale là fuori.


Quindi, se ho un elenco che sto usando nella mia chiamata Update () ogni volta, tutti gli elementi in esso in cui l'elemento è "null" verranno automaticamente ripuliti e rimossi?
Mathias Lykkegaard Lorenzen,

@Mathias No. Vedi il mio commento sulla tua domanda. Il garbage collector non modificherà nulla a cui è ancora possibile accedere, inclusi la raccolta e i relativi contenuti.
Andrew Russell,

1
La domanda non ha nulla a che fare con GC, tranne la sfortunata scelta della parola "spazzatura".
Nevermind,
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.