¿Deshacerse de objetos muertos en un juego de manera efectiva?


10

Estoy usando un bucle for o foreach (no importa) para recorrer todos mis objetos cuando necesitan ser actualizados o dibujados. Sin embargo, cuando se mata un objeto, quiero que se elimine de la colección nuevamente.

Hago esto agregando el objeto a una lista de objetos muertos, y luego, cuando todo se ha dibujado y actualizado, reviso todo en esta lista, eliminando también los objetos de la lista de la fuente original.

¿Hay una mejor manera de hacer esto?



2
Esto realmente no tiene nada que ver con la recolección de basura, a pesar del uso de la palabra "basura" en el título y las respuestas hasta ahora que han mencionado .NET Garbage Collector.
Andrew Russell

Me tomé la libertad de cambiar la palabra "basura" a "muerto", para evitar confusiones con el recolector de basura .NET.
importa

Oh, soy consciente de eso. El artículo de recolección de basura sigue siendo relevante porque puede jugar con lo que haces con esos objetos muertos. Por ejemplo, restos muertos? Desvincule de todo, reinícielo a los valores predeterminados, vuelva a colocarlo en el cubo de escombros.
doppelgreener

Respuestas:


11

En primer lugar: terminología. Si dices "lista de basura", la gente pensará que estás hablando del recolector de basura. Llamémosle la "lista muerta".

Como probablemente haya descubierto, de ahí su pregunta, no puede eliminar elementos de una colección mientras está iterando a través de ella. Si su colección es una, List<>entonces la forma más sencilla de eliminar elementos es:

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

Tenga en cuenta que itera hacia atrás . Puede eliminar elementos mientras itera hacia adelante, pero es más complicado y requiere a goto. No puedes hacerlo con foreach.

Puede realizar la eliminación por separado de su ciclo de actualización. O puede fusionarlo en su ciclo de actualización. Siempre haga lo siguiente RemoveAtal final del ciclo; después de ese punto en el ciclo, list[i]no puede considerarse válido (vuelve a ser válido en la próxima iteración).

Tenga en cuenta, además, que cada objeto tiene su propia IsDeadbandera (si está utilizando el patrón de eliminación, podría hacerlo IsDisposed): en realidad no necesita mantener una "lista muerta".

Es preferible usar una bandera en cada elemento para el rendimiento, ya que evita tener que buscar en la lista para realizar la eliminación. Y también es preferible para el diseño, significa que cada objeto puede verificar fácilmente si está muerto, por lo que no llama accidentalmente a ningún método (si estos objetos están implementando el patrón desechable, podrían arrojarse ObjectDisposedExceptionen este caso).

Si tiene una colección distinta de a List<>, entonces eliminar elementos muertos de esa colección aún puede implicar la creación de una lista muerta (ya que la eliminación durante la iteración puede no ser posible). En mi opinión, es mejor, en cuanto al diseño, crear la lista muerta justo antes de que se use, iterando sobre la colección buscando IsDeadbanderas, en lugar de tratar de mantener la lista a medida que se eliminan los objetos.

Una vez que haya terminado con una lista muerta, debe Clear()hacerlo, y luego guardarla para reutilizarla más adelante.


¿Entonces el patrón desechable se considera más "efectivo"?
Jonathan Connell

@ Jonathan: No entiendo tu pregunta. El IsDeadpatrón es funcionalmente idéntico a IsDisposed. Usar semánticamente IsDeadimplica que mantendrá una lista de Draw / Update de estos objetos que tendrá elementos muertos eliminados en un momento posterior. El patrón de eliminación no implica eso. Por otra parte, el patrón de la disposición no implica que usted pueda tener recursos no administrados en poder de ese objeto (que por lo general no es apropiado para objetos de juego).
Andrew Russell

Puedes hacerlo con foreach usando el reverso.
shinzou

0

El 99.9% de las veces, el recolector de basura (GC) se encargará de todo sin problemas. Simplemente deje que haga su trabajo una vez que haya eliminado / anulado esos objetos. Hay una latencia en la limpieza que depende de una serie de factores (cuántos bloques de memoria se han asignado, por ejemplo), pero normalmente no es necesario que lo sepas, y mucho menos que te preocupes. Está diseñado para funcionar solo. Cuál es una razón por la que estás usando C # y no C.

En cuanto al rendimiento de GC en general, no se preocupe demasiado a menos que sepa (a través de la creación de perfiles) que está creando un cuello de botella; esto es solo en juegos bastante exigentes, en general. Si este es el caso, mire GC.Collect () y sus diversas dificultades, para saber cuándo es apropiado usarlo. Sugiero buscar en Google "force gc xna", hay mucho material por ahí.


Entonces, si tengo una lista que estoy usando en mi llamada Update () cada vez, ¿todos los elementos en los que el elemento es "nulo" se limpiarán y eliminarán automáticamente?
Mathias Lykkegaard Lorenzen

@Mathias No. Vea mi comentario sobre su pregunta. El recolector de basura no modificará nada a lo que todavía pueda acceder, incluida su colección y su contenido.
Andrew Russell

1
La pregunta no tiene nada que ver con GC, excepto la desafortunada elección de la palabra "basura".
importa
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.