Tote Gegenstände in einem Spiel effektiv loswerden?


10

Ich verwende eine for-Schleife oder eine foreach-Schleife (spielt keine Rolle), um alle meine Objekte zu durchlaufen, wenn sie aktualisiert oder gezeichnet werden müssen. Wenn ein Objekt jedoch getötet wird, möchte ich, dass es wieder aus der Sammlung entfernt wird.

Dazu füge ich das Objekt einer Liste mit toten Objekten hinzu. Wenn dann alles gezeichnet und aktualisiert wurde, gehe ich alles in dieser Liste durch und entferne auch die Objekte in der Liste aus der Originalquelle.

Gibt es einen besseren Weg, dies zu tun?



2
Dies hat wirklich nichts mit Garbage Collection zu tun, trotz der Verwendung des Wortes "Garbage" im Titel und der bisherigen Antworten, die den .NET Garbage Collector erwähnt haben.
Andrew Russell

Ich habe mir erlaubt, das Wort "Müll" tatsächlich in "tot" zu ändern, um Verwechslungen mit .NET Garbage Collector zu vermeiden.
Nevermind

Oh, das ist mir bewusst. Der Garbage Collection-Artikel ist immer noch relevant, da er möglicherweise dazu beiträgt, was Sie mit diesen toten Objekten tun. ZB tote Trümmer? Trennen Sie es von allem, setzen Sie es auf die Standardeinstellungen zurück und legen Sie es wieder in den Schmutzbehälter.
Doppelgreener

Antworten:


11

Zuallererst: Terminologie. Wenn Sie "Müllliste" sagen, denken die Leute, Sie sprechen über den Müllsammler. Nennen wir es die "tote Liste".

Wie Sie wahrscheinlich festgestellt haben, können Sie daher bei Ihrer Frage keine Elemente aus einer Sammlung entfernen, während Sie diese durchlaufen. Wenn es sich bei Ihrer Sammlung um eine handelt List<>, können Sie Elemente am einfachsten entfernen:

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

Beachten Sie, dass Sie rückwärts iterieren . Sie können Elemente entfernen, während Sie vorwärts iterieren, aber es ist unordentlicher und erfordert a goto. Sie können es nicht mit tun foreach.

Sie können das Entfernen entweder separat von Ihrer Aktualisierungsschleife durchführen. Oder Sie können es in Ihre Update-Schleife einfügen. Führen Sie dies immer RemoveAtam Ende der Schleife durch - nach diesem Punkt in der Schleife list[i]kann dies nicht als gültig angesehen werden (es wird bei der nächsten Iteration wieder gültig).

Beachten Sie auch, dass jedes Objekt ein eigenes IsDeadFlag hat (wenn Sie das Entsorgungsmuster verwenden, können Sie es erstellen IsDisposed) - Sie müssen überhaupt keine "tote Liste" führen.

Die Verwendung eines Flags für jedes Element ist für die Leistung vorzuziehen, da Sie nicht die Liste durchsuchen müssen, um das Entfernen durchzuführen. Und es ist auch für das Design vorzuziehen - es bedeutet, dass jedes Objekt leicht überprüfen kann, ob es tot ist - damit Sie nicht versehentlich Methoden darauf aufrufen (wenn diese Objekte das Einwegmuster implementieren, können sie ObjectDisposedExceptionin diesem Fall werfen ).

Wenn Sie eine andere Sammlung als a haben List<>, muss beim Entfernen toter Elemente aus dieser Sammlung möglicherweise noch eine tote Liste erstellt werden (da das Entfernen während der Iteration möglicherweise nicht möglich ist). Meiner Meinung nach ist es in Bezug auf das Design IsDeadbesser , die Dead List direkt vor ihrer Verwendung zu erstellen, indem Sie die Sammlung nach Flags durchsuchen, anstatt zu versuchen, die Liste beizubehalten, wenn Objekte getötet werden.

Sobald Sie mit einer toten Liste fertig sind, sollten Sie Clear()sie aufbewahren und dann aufbewahren, um sie später wiederzuverwenden.


Das Einwegmuster wird dann als "effektiver" angesehen?
Jonathan Connell

@ Jonathan: Ich verstehe deine Frage nicht. Das IsDeadMuster ist funktional identisch mit IsDisposed. Die semantische Verwendung IsDeadbedeutet, dass Sie eine Liste zum Zeichnen / Aktualisieren dieser Objekte verwalten, aus der zu einem späteren Zeitpunkt tote Elemente entfernt werden. Das Entsorgungsmuster impliziert dies nicht. Darüber hinaus ist die Entsorgung Muster nicht bedeuten , dass Sie von diesem Objekt gehalten verwalteten Ressourcen haben (was für Gameplay - Objekte in der Regel nicht geeignet ist).
Andrew Russell

Sie können dies mit foreach tun, indem Sie reverse verwenden.
Shinzou

0

In 99,9% der Fälle kümmert sich der Garbage Collector (GC) problemlos um alles. Lassen Sie es einfach seine Arbeit machen, sobald Sie diese Objekte gelöscht / auf Null gesetzt haben. Es gibt eine Latenz bei der Bereinigung, die von einer Reihe von Faktoren abhängt (wie viele Speicherblöcke beispielsweise zugewiesen wurden), aber Sie müssen normalerweise nicht einmal darüber Bescheid wissen, geschweige denn darüber nachdenken. Es ist so konzipiert, dass es nur funktioniert. Welches ist ein Grund, warum Sie C # und nicht C verwenden.

Bezüglich der GC-Leistung im Allgemeinen sollten Sie sich nicht übermäßig Sorgen machen, es sei denn, Sie wissen (durch Profilerstellung), dass dies zu einem Engpass führt - dies ist im Allgemeinen nur bei recht anspruchsvollen Spielen der Fall. Wenn dies der Fall ist, schauen Sie sich GC.Collect () und seine verschiedenen Fallstricke an, damit Sie wissen, wann es angemessen ist, es zu verwenden. Ich schlage vor, "force gc xna" zu googeln, da draußen gibt es viel Material.


Wenn ich also eine Liste habe, die ich jedes Mal in meinem Update () -Aufruf verwende, werden alle Elemente darin, in denen das Element "null" ist, automatisch bereinigt UND entfernt?
Mathias Lykkegaard Lorenzen

@ Mathias Nein. Siehe meinen Kommentar zu Ihrer Frage. Der Garbage Collector ändert nichts, auf das Sie noch zugreifen können - einschließlich Ihrer Sammlung und ihres Inhalts.
Andrew Russell

1
Die Frage hat nichts mit GC zu tun, außer der unglücklichen Wahl des Wortes "Müll".
Nevermind
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.