Jump to content
Unity Insider Forum

Objekte in einer Liste zerstören


Singular

Recommended Posts

Hey Leute ich schon wieder,

dieses mal habe ich Probleme mit einer meiner Listen. Ich habe eine Liste mit GameObjects, die am Ende des Levels zerstört werden sollen, damit das nächste Level geladen werden kann. (Die weiteren Level Befinden sich in der selben Scene, deswegen muss ich einmal aufräumen.^^ )

Dafür habe ich folgende Zeilen geschrieben:

    private void NextLevel()    // Wird aufgerufen, um die Scene leer zu räumen
    {
        if (balls.Count > 0)        // wird nur ausgeführt, wenn mindestens ein Ball auf dem Spielfeld ist
        {
            List<GameObject> ballsTemp = balls;     // erstellt eine temporäre liste von balls

            foreach (GameObject ball in ballsTemp)  // für jedes Objekt in der erstellten Liste
            {
                Ball b = ball.GetComponent<Ball>();     // hole dir das angehängte Script
                b.resetMultiplier();                   // schreibt dem Spieler die Punkte gut, die der Ball noch hat.
                b.DestroyMe(1);                         // zerstört den Ball und löscht ihn aus der Liste "balls"
            }
        }
    }

die Methode resetMultiplier() ist nicht von belang. hier aber noch die Methode DestroyMe() löscht lediglich den Ball aus der Liste balls, die ich zu beginn von NextLevel() kopiert habe.

 

Es wird jedoch nur der erste Ball zerstört und danach bekomme ich folgenden Fehler:

InvalidOperationException: Collection was modified; enumeration operation may not execute.
System.ThrowHelper.ThrowInvalidOperationException (System.ExceptionResource resource) (at <eae584ce26bc40229c1b1aa476bfa589>:0)
System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () (at <eae584ce26bc40229c1b1aa476bfa589>:0)
System.Collections.Generic.List`1+Enumerator[T].MoveNext () (at <eae584ce26bc40229c1b1aa476bfa589>:0)
GameManager.NextLevel () (at Assets/Scripts/GameManager.cs:146) <-- Die oben erwähte foreach Schleife.
GameManager.Update () (at Assets/Scripts/GameManager.cs:96)    <-- Ruft NextLevel() auf

Danke für eure Hilfe.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo

 

Und der Fehler hast du sogar zitiert:

InvalidOperationException: Collection was modified; enumeration operation may not execute.

Du kannst eine Liste nicht modifizieren, während du über sie iterierst. Ich nehme bei so etwas oft den ersten Text und gebe ihn mal google. Da kommen dann meist Treffer. In deinem Fall kenne ich DestroyMe() nicht, würde aber das GameObject zerstören und dann die Liste per Clear() löschen.

 

Christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor einer Stunde schrieb chrische5:

Du kannst eine Liste nicht modifizieren, während du über sie iterierst.

Dazu sei gesagt, dass sich dieser Satz auf Iteratoren bezeichnet. Die foreach-Schleife benutzt einen solchen Iterator. Eine for-Schleife tut das nicht. Du kannst also statt deiner foreach-Schleife eine for-Schleife benutzen und dann gemächlich mit [i] auf die Listenelemente zugreifen. Wenn die Objekte sich in DestroyMe selbst aus der Liste austragen (wovon ich ausgehe), dann kannst du einfach i runter statt hochzählen, dann musst du da auch nicht zu doll auf den Index aufpassen.

Es sei außerdem gesagt, dass das Zerstören eines Objekts keinen Einfluss auf irgendeine Liste hat, in der sich das Objekt befindet. Dass du diese Exception kriegst, passiert nur, weil du in DestroyMe, OnDestroy oder OnDisable irgendwo stehen hast, dass sich das Objekt von der Liste abmeldet.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Okay, aber genau deswegen erstelle ich ja eine Temporäre Liste, von deraus die Objekte aufgerufen und zerstört werden. Aus dieser Liste werden die Objekte nicht abgemeldet. Da fliegen mir dann (oder sollten eigentlich, wenn ich keinen Fehler bekommen würde) ein paar "Missing Objects" herum, die mir dann egal sein können, da die Liste am Ende eh wieder gelöscht wird...

Bedeutet aber im Umkehrschluss, ich kann eine foreach Schleife einer Liste NICHT nutzen um Objekte aus einer Liste zu Zerstören, weil dann ein neues (in dem Fall dan ein Missing Object") auftaucht, über das er dann auch noch itterieren müsste? 

EDIT: Okay, Nein Zerstören geht, aber list.Remove geht nicht. Allerdings selbst mit der alten Liste:

public class Test : MonoBehaviour
{
    public List<GameObject> list;

    void Start()
    {
        List<GameObject> listTemp = list;
        foreach(GameObject g in listTemp)
        {
            list.Remove(g);			//<-- Geht nicht
            Destroy(g);				//<-- Geht
        }
    }
}

Wobei ich mich ein wenig wundere, weil ich in meinem Test die erste Liste nehme, die ich ja nicht in der foreach-schleife aufgerufen wird. Dann stellt sich für mich die Ftrage, was ist das für eine listTemp, die dort erstellt wird? Ist das nur ein Pointer, der dann doch nur auf list zeigt, bzw eine Liste von Pointern, die auf die GOs zeichen? Selbst wenn ich das ganze noch erweitere:

        [...]

	List<GameObject> listTemp = new List<GameObject>();
        listTemp = list;

	foreach[...]

tritt der Fehler auf.

oder:

public class Test : MonoBehaviour
{
    public List<GameObject> list;
    List<GameObject> listTemp;

    void Start()
    {
        listTemp = list;
        foreach [...]

Alle erzeugen den Fehler.

 

Ich denke ich werde dann werde ich die erwähte for-schleife nehmen, die rückwärts durch meine Objekte geht. Danke euch.

Link zu diesem Kommentar
Auf anderen Seiten teilen

🙄

Ja du hast was übersehen... DAS WÄRE JA ZU EINFACH...!!! :D:D;) 

😄 tatsächlich wäre das jetzt sicherlich eine Laufzeitanalyse wert, zu schaunen was weniger Zeit frist... Die Bälle auf dem Spielfeld sammeln Punkte. Wenn der Spieler aber schon gewonnen hat, müssen diese Punkte noch eingesammelt werden --> daher die Methoder "resetMultipier()". Heißt also ich müsste dennoch eine foreach schleife machen um die Punkte einzusammeln und anschließend die Bälle zerstören um dann wieder die Liste zu leeren... wären also:

- foreach um die Punkte einzusammeln und die Objekte zu zerstören
- list.clear ist unterm strich wahrscheinlich auch nochmal eine fo Schleife...

Wahrscheinlich auch eine Option, aber wenn ich das ganze mir einem Schleifendurchlauf mache, in dem ich eh die Objekte in der Hand habe, macht das am meisten Sinn denke ich. Aber unterm Strich hast du recht :D list.clear am ende würde auch gehen tatsächlich.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 15 Stunden schrieb Singular:

Okay, Nein Zerstören geht, aber list.Remove geht nicht.

Sag ich ja :)

vor 18 Stunden schrieb Sascha:

Es sei außerdem gesagt, dass das Zerstören eines Objekts keinen Einfluss auf irgendeine Liste hat, in der sich das Objekt befindet. Dass du diese Exception kriegst, passiert nur, weil du in DestroyMe, OnDestroy oder OnDisable irgendwo stehen hast, dass sich das Objekt von der Liste abmeldet.

 

vor 15 Stunden schrieb Singular:

Ist das nur ein Pointer, der dann doch nur auf list zeigt,

Exakt. Du hast zwei Variablen, in denen dieselbe Adresse steht, unter der dein eines List-Objekt zu finden ist.

vor 14 Stunden schrieb Singular:

list.clear am ende würde auch gehen tatsächlich.

Ja - wenn du bereit bist, deinen anderen Code zu ändern. Du machst ja nicht list.Remove nicht direkt in der Schleife, sondern wie gesagt vermutlich in OnDisable oder OnDestroy oder so. Das kann schon gut und richtig sein, und wenn du das beibehalten willst, kannst du einfach zur runterzählenden for-Schleife wechseln.

for (var i = list.Count - 1; i >= 0; i--)
{
  Destroy(list[i]); // <- löst indirekt list.Remove(list[i]) aus
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 17 Stunden schrieb chrische5:

Am ende wird so etwas wahrscheinlich egal sein. ich gestehe, dass ich meist so etwas wie clear nutze, weil ich davon ausgehe, dass das bestmöglich geht. kommt natürlich immer auf das genaue szenario an.

Mein Spiel soll auf dem Handy laufen. Ich will da keine großen Spielereien haben sondern kurz und klaren Code, der nicht zuviel unnötigen Kram macht. (Ich haße es, wenn bei Spielen mein Handy zu heiß wird) Wie gesagt an die Clear Methode habe ich gar nicht mehr gedacht aber, da ich nochmal alle Bälle aufrufen muss um die Punkte einzusammeln, dann kann ich in dem Zug sie gleich aus der Liste austragen und sie danach zerstören. Ob ich das vorwärts oder Rückwärts tue ist den Bällen zum glück egal.^^

 

vor 3 Stunden schrieb Sascha:

Exakt. Du hast zwei Variablen, in denen dieselbe Adresse steht, unter der dein eines List-Objekt zu finden ist.

Okay, dann bringt es nichts auf diese Art und weise eine Liste zu Kompieren. Das müsste ich dann wohl auch wieder mit einer for-Schleife lösen und jeden einzelnen Wert in die neue Liste übertragen oder gibt es eine bereits implemetierte Methode eine Liste zu Kopieren?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du kannst eine Sammlung in den Konstruktor einer neuen Liste übergeben, dann kriegt die neue Liste dieselben Elemente. Aber warum solltest du mühsam eine Liste erstellen lassen, da den Inhalt reinkopieren und am Ende mit Garbage da sitzen, wenn du das auch einfach auf der existierenden Liste regeln kannst?

vor 17 Minuten schrieb Singular:

Mein Spiel soll auf dem Handy laufen. Ich will da keine großen Spielereien haben sondern kurz und klaren Code, der nicht zuviel unnötigen Kram macht. (Ich haße es, wenn bei Spielen mein Handy zu heiß wird)

Ich hoffe wirklich, du suggerierst damit nicht, dass mehr Zeilen Code dein Handy heiß werden lassen? o.O

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...