Se débarrasser efficacement des objets morts dans un jeu?

10

J'utilise une boucle for ou une boucle foreach (peu importe) pour parcourir tous mes objets lorsqu'ils doivent être mis à jour ou dessinés. Cependant, lorsqu'un objet est tué, je veux qu'il soit à nouveau supprimé de la collection.

Je fais cela en ajoutant l'objet à une liste d'objets morts, puis, lorsque tout a été dessiné et mis à jour, je passe en revue tout ce qui se trouve dans cette liste, en supprimant également les objets de la liste de la source d'origine.

Existe-t-il une meilleure façon de le faire?

Mathias Lykkegaard Lorenzen
la source
2
Cela n'a vraiment rien à voir avec la collecte des ordures, malgré l'utilisation du mot "ordures" dans le titre et les réponses jusqu'à présent qui ont mentionné le garbage collector .NET.
Andrew Russell
J'ai pris la liberté de changer le mot "poubelle" en "mort", pour éviter toute confusion avec le garbage collector .NET.
Nevermind
Oh, j'en suis conscient. L'article de récupération de place est toujours pertinent car il peut jouer sur ce que vous faites avec ces objets morts. Par exemple, des débris morts? Dissociez-le de tout, réinitialisez-le aux valeurs par défaut, remettez-le dans le seau à débris.
doppelgreener

Réponses:

11

Tout d'abord: la terminologie. Si vous dites "liste des ordures", les gens penseront que vous parlez du ramasse-miettes. Appelons cela la "liste morte".

Comme vous l'avez probablement découvert, d'où votre question, vous ne pouvez pas supprimer des éléments d'une collection pendant que vous parcourez celle-ci. Si votre collection est une, List<>la façon la plus simple de supprimer des éléments est:

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

Notez que vous répétez en arrière . Vous pouvez supprimer des éléments tout en itérant vers l'avant, mais c'est plus compliqué et nécessite un goto. Vous ne pouvez pas le faire avec foreach.

Vous pouvez soit effectuer la suppression séparément de votre boucle de mise à jour. Ou vous pouvez le fusionner dans votre boucle de mise à jour. Faites toujours le RemoveAtà la fin de la boucle - après ce point de la boucle, list[i]ne peut pas être considéré comme valide (il redevient valide à la prochaine itération).

Notez également que chaque objet a son propre IsDeadindicateur (si vous utilisez le modèle d'élimination, vous pouvez le créer IsDisposed) - vous n'avez pas du tout besoin de maintenir une "liste morte".

L'utilisation d'un indicateur sur chaque élément est préférable pour les performances, car vous évitez d'avoir à parcourir la liste pour effectuer la suppression. Et il est également préférable pour la conception - cela signifie que chaque objet peut facilement vérifier s'il est mort - afin de ne pas appeler accidentellement de méthodes dessus (si ces objets implémentent le modèle jetable, ils pourraient le lancer ObjectDisposedExceptiondans ce cas).

Si vous avez une collection autre que a List<>, la suppression des éléments morts de cette collection peut toujours impliquer la création d'une liste morte (car la suppression pendant l'itération peut ne pas être possible). À mon avis, il est plus agréable, au niveau de la conception, de créer la liste morte juste avant qu'elle ne soit utilisée, en parcourant la collection à la recherche de IsDeaddrapeaux, plutôt qu'en essayant de maintenir la liste lorsque des objets sont tués.

Une fois que vous avez terminé avec une liste morte, vous devez la Clear()conserver, puis la conserver pour la réutiliser plus tard.

Andrew Russell
la source
Le modèle jetable est donc considéré comme plus «efficace»?
Jonathan Connell
@Jonathan: Je ne comprends pas votre question. Le IsDeadmotif est fonctionnellement identique à IsDisposed. L'utilisation sémantique IsDeadimplique que vous maintenez une liste de tirage / mise à jour de ces objets dont les éléments morts seront supprimés ultérieurement. Le modèle d'élimination n'implique pas cela. De plus, le modèle d'élimination n'implique que vous pouvez disposer de ressources non gérés détenues par cet objet (qui est généralement pas approprié pour les objets de gameplay).
Andrew Russell
Vous pouvez le faire avec foreach en utilisant reverse.
shinzou
0

99,9% du temps, le ramasse-miettes (GC) s'occupe de tout sans tracas. Laissez-le faire son travail une fois que vous avez supprimé / annulé ces objets. Il y a une latence dans le nettoyage qui dépend d'un certain nombre de facteurs (combien de blocs de mémoire ont été affectés par exemple) mais vous n'avez généralement pas besoin de le savoir, et encore moins de vous en soucier. Il est conçu pour fonctionner. C'est une des raisons pour lesquelles vous utilisez C # et non C.

Concernant les performances du GC en général, ne vous inquiétez pas trop à moins que vous ne sachiez (par profilage) que cela crée un goulot d'étranglement - ce n'est que dans les jeux assez exigeants, en général. Si tel est le cas, examinez GC.Collect (), et ses divers pièges, afin de savoir quand il convient de l'utiliser. Je suggère de googler "force gc xna", il y a beaucoup de matériel là-bas.

Ingénieur
la source
Donc, si j'ai une liste que j'utilise à chaque fois dans mon appel Update (), tous les éléments dans lesquels l'élément est "null" seront automatiquement nettoyés ET supprimés?
Mathias Lykkegaard Lorenzen
@Mathias No. Voir mon commentaire sur votre question. Le garbage collector ne modifiera rien de ce que vous pouvez toujours accéder, y compris votre collection et son contenu.
Andrew Russell
1
La question n'a rien à voir avec GC, sauf le malheureux choix du mot "poubelle".
Nevermind