Reputation: 15
In my Unity project I have a LevelGenerator
creating Stages
(game objects) and adding these to a list. When I am dying I want to destroy and remove all items in the list. But when I am iterating through the list, only the first object is destroyed and removed, then an error happens:
InvalidOperationException: Collection was modified; enumeration operation may not execute.
System.ThrowHelper.ThrowInvalidOperationException (System.ExceptionResource resource) (at :0)
System.Collections.Generic.List1+Enumerator[T].MoveNextRare () (at <fb001e01371b4adca20013e0ac763896>:0) System.Collections.Generic.List
1+Enumerator[T].MoveNext () (at :0)
Could you help me?
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;
}
}
Inside the error script:
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);
}
...
}
...
}
Upvotes: 1
Views: 2478
Reputation: 2946
Your problem is you're removing items from a list as you're iterating over them, and this changes the size of the list!
The easiest way to do this would probably be
foreach (GameObject toDestroy in destroyEnviroment.gameObjectsToDestroy)
{
Destroy(toDestroy);
}
destroyEnviroment.gameObjectsToDestroy.Clear();
Edit:
It's worth noting that here I Destroy
before removing, this might seem counter-intuitive but the reason is calling destroy will set the object to be destroyed at the end of the frame, we can then forget about it and clear the list - and everything else will be taken care of.
I'm a big advocate of not removing items from lists if you don't need to - in this case you're clearing the whole list so you might as well use clear()
Upvotes: 1
Reputation: 964
Anytime you see InvalidOperationException: Collection was modified; enumeration operation may not execute
where the collection in question is being accessed on a single thread you should know that it is because you are modifying the same list for which you are iterating over
This often happens when making use of a functionality that uses IEnumerable
such as:
foreach
loops as in your caseGetEnumerator()
directly and using that to iterate through the collectionThe common solution in the case of 'foreach' loops is to consider a different construct that makes sense to your problem.
I can also provide a method of applying this in your case if needed. Just let me know.
Upvotes: 0