【问题标题】:How to solve InvalidOperationException?如何解决 InvalidOperationException?
【发布时间】:2021-03-18 07:07:01
【问题描述】:

在我的 Unity 项目中,我有一个 LevelGenerator 创建 Stages(游戏对象)并将这些添加到列表中。当我快死时,我想销毁并删除列表中的所有项目。但是当我遍历列表时,只有第一个对象被销毁和删除,然后发生错误:

InvalidOperationException:集合已修改;枚举操作可能无法执行。
System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource 资源)(在:0)
System.Collections.Generic.List1+Enumerator[T].MoveNextRare () (at <fb001e01371b4adca20013e0ac763896>:0) System.Collections.Generic.List1+Enumerator[T].MoveNext () (at :0)

你能帮帮我吗?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LevelGenerator : MonoBehaviour
{
    private const float PLAYER_DISTANCE_SPAWN_LEVEL_PART  = 25f;

    [SerializeField] private Transform Startingstage;
    [SerializeField] private List<Transform> StageList;
    [SerializeField] private Transform player;

    public List<GameObject> gameObjectsToDestroy;
    public bool creatingGoesOn = false;

    private Vector3 lastEndPosition;

    public void CreateEnviroment()  // Every time called when game is started
    {
        lastEndPosition = Startingstage.Find("EndingPoint").position;

        int startingSpawnLevelParts = 7;

        for (int i = 0; i < startingSpawnLevelParts; i++)
        {
            SpawnLevelPart();
        }

        creatingGoesOn = true;
    }

    private void Update()
    {
        if (creatingGoesOn)
        {
            if (Vector3.Distance(player.position, lastEndPosition) < PLAYER_DISTANCE_SPAWN_LEVEL_PART)
            {
                // Spawn a new level part if player is to close to the end.
                SpawnLevelPart();
            }
        }
    }

    private void SpawnLevelPart()
    {
        Transform randomStage = StageList[Random.Range(0, StageList.Count)];
        Transform lastLevelPartTransform = SpawnLevelPart(randomStage, lastEndPosition);
        lastEndPosition = lastLevelPartTransform.Find("EndingPoint").position;
    }

    private Transform SpawnLevelPart(Transform stage, Vector3 spawnPosition)
    {
        Transform levelPartTransform = Instantiate(stage, spawnPosition,  Quaternion.identity);
        gameObjectsToDestroy.Add(levelPartTransform.gameObject);
        return levelPartTransform;
    }
}

错误脚本内部:

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.transform.CompareTag("Player"))
    {
        destroyEnviroment.creatingGoesOn = false;
        Debug.Log(destroyEnviroment.gameObjectsToDestroy.Count);

        foreach (GameObject toDestroy in destroyEnviroment.gameObjectsToDestroy)
        {
            destroyEnviroment.gameObjectsToDestroy.Remove(toDestroy);
            Destroy(toDestroy); //Somewhere here is the error
            Debug.Log(destroyEnviroment.gameObjectsToDestroy.Count);
        }
        ...
    }
    ...
}

【问题讨论】:

  • foreach (GameObject toDestroy in destroyEnviroment.gameObjectsToDestroy.ToList())

标签: c# arrays unity3d


【解决方案1】:

您的问题是您在迭代列表时从列表中删除它们,这会改变列表的大小!

最简单的方法可能是

foreach (GameObject toDestroy in destroyEnviroment.gameObjectsToDestroy)
{
    Destroy(toDestroy);
}
destroyEnviroment.gameObjectsToDestroy.Clear();

编辑:
值得注意的是,在这里我在删除之前Destroy,这可能看起来违反直觉,但原因是调用destroy会将对象设置为在帧结束时被销毁,然后我们可以忘记它并清除列表 - 和其他一切都会得到照顾。
如果您不需要,我强烈主张不要从列表中删除项目 - 在这种情况下,您正在清除整个列表,因此您不妨使用 clear()

【讨论】:

  • 谢谢。但是为什么不使用 while 循环(list.Count > 0)来销毁并从列表中删除元素 [0] 呢?在我的情况下,它总是出现:“不在范围内”。
  • @Thboss 我会更新我的答案来回应这个
【解决方案2】:

任何时候你看到InvalidOperationException: Collection was modified; enumeration operation may not execute 有问题的集合在单个线程上被访问,你应该知道这是因为你正在修改你正在迭代的同一个列表

在使用使用IEnumerable 的功能时经常发生这种情况,例如:

  • foreach 在你的情况下循环
  • 直接GetEnumerator() 并使用它来遍历集合

“foreach”循环的常见解决方案是考虑一种对您的问题有意义的不同构造。

如果需要,我还可以提供一种在您的情况下应用此方法的方法。请告诉我。

【讨论】:

  • 但在这种情况下,您实际上并不需要更改循环类型,Jay 建议在循环之外清除它就足够了。有时它在某些情况下可能不起作用。如果您知道要清除列表中的每个项目,我倾向于使用的另一个选项是while(collection.Any)。请记住,如果您要从头开始删除元素,例如 List,则某些数据类型可能是 O(n)
  • 谢谢。我是 Unity 的新手,这极大地推动了我前进。你最好!
猜你喜欢
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多