Avoiding/Removing the InvalidOperationException Error.

I’ve written a chunk of code in a foreach statement, so that I can add and remove items to a List, and whilst they are on the list they will move at a consistent rate and will remove themselves from the list when they reach their destination. I’ve encountered a problem, however, where Unity is telling me off and pausing every time the list becomes empty, with the following error:

I will attach the code below (although it’s fairly long and probably a bit laborious to read through), but even by putting an if-statement around the entire foreach loop (ensuring that it only tries to run whilst there are GameObjects in the list), it still kicks off with the error every time the list becomes empty…

Any suggestions or workarounds for this problem would be much appreciated. Code attached below:

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

public class GameManager : MonoBehaviour {

    public static List<int> cardStack1 = new List<int>();
    public static List<int> cardStack2 = new List<int>();
    public static List<int> cardStack3 = new List<int>();
    public static List<int> cardStack4 = new List<int>();

    public GameObject[] cardPosons;
    public static GameObject[] cardPosns;

    //each script can choose speed. Most(if not all) will pull this value.
    public static float cardSpeed = 5;


    private static List<GameObject> movingCards = new List<GameObject>();
    private static List<float> movingCardSpeed = new List<float>();
    private static List<Vector3> movingCardGoal = new List<Vector3>();
    private static float speedMultiplier = 0.1f;


    void Start() {
        cardPosns = cardPosons;
    }

    void Update() {
        StepMovement();
    }

    private static void StepMovement() {
        int counter = -1;
        if (movingCards != null) {
            foreach (GameObject g in movingCards) {
                counter++;
                Transform currentLocal = movingCards[counter].transform;
                float speed = movingCardSpeed[counter] * speedMultiplier;
                Vector3 remainingDistance = movingCardGoal[counter] - movingCards[counter].transform.position;

                // Individualize remainingDistance's floats, turn all positive.
                float remX = remainingDistance.x; if (remX < 0) { remX = -remX; }
                float remY = remainingDistance.y; if (remY < 0) { remY = -remY; }
                float remZ = remainingDistance.z; if (remZ < 0) { remZ = -remZ; }

                float maxAxisLength = 0;
                if (remX >= remY && remX >= remZ) { maxAxisLength = remX; }
                if (remY >= remX && remY >= remZ) { maxAxisLength = remY; }
                if (remZ >= remX && remZ >= remY) { maxAxisLength = remZ; }

                if (maxAxisLength == 0) { print("Failure lines 41-44, remainingDistance: " + remainingDistance); }

                if (speed > maxAxisLength) { speed = maxAxisLength; }

                float xResult = speed * (remainingDistance.x / maxAxisLength);
                float yResult = speed * (remainingDistance.y / maxAxisLength);
                float zResult = speed * (remainingDistance.z / maxAxisLength);

                currentLocal.position += new Vector3(xResult, yResult, zResult);
                movingCards[counter].transform.position = currentLocal.position;

                if (currentLocal.position == movingCardGoal[counter]) {
                    // Attempt to remove gameobject, speed and goal from respective lists on completion.
                    movingCards.Remove(movingCards[counter]);
                    movingCardSpeed.Remove(movingCardSpeed[counter]);
                    movingCardGoal.Remove(movingCardGoal[counter]);
                }
            }
        }
    }

    public static void MoveCard(GameObject card, int goal, float speed) {
        Transform cardStartPosition = card.transform;
        Transform goalLocation = cardPosns[goal].transform;
        float posOfset = card.GetComponent<Card>().CardHeightOffset();

        Vector3 ofsetPosition = new Vector3(goalLocation.position.x, goalLocation.position.y + +posOfset, goalLocation.position.z);

        if (speed == 0) {
            card.transform.position = ofsetPosition;
            card.transform.rotation = goalLocation.rotation;
        }
        else {
            movingCards.Add(card);
            movingCardSpeed.Add(speed);
            movingCardGoal.Add(ofsetPosition);
        }
    }
}

It’s against the rules to modify a collection if you’re iterating through it with an enumerator.

1 Like

You can either switch to a for-loop, since you use a counter anyway. You’d then have to adjust the index for the iteration whenever you insert or remove elements previous to the iteration index, so that you prevent the loop from skipping elements, running out of bounds and handling an element twice.

Or simply create a shallow copy of the list and manipulate the original while you iterate the copied list.

I would also recommend to create a seperate type for the lists that you’re trying to keep in sync. That would simplify your code as well as eliminate a little of overhead, as you’d only manipulate a list of the new type, not N lists that you’re trying to keep in sync.

1 Like

Found a nice solution, by just replacing the foreach loop with a for-loop, yet not needing to index the lists:

On line 36,
replaced: foreach (GameObject g in movingCards) {
with this: for (int i = 0; i < movingCards.Count; i++) {

depressingly was that simple. Thanks for the help though!