Search Unity

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

Discussion in 'Scripting' started by squirrelnuts987, Jun 8, 2021.

  1. squirrelnuts987

    squirrelnuts987

    Joined:
    Feb 9, 2021
    Posts:
    4
    I saw a tutorial for a shotgun script that I didn't know how to do, and I have limited c# knowledge. I have tried this many times, but I get the same error every time I click Fire1:

    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)
    ShotgunBehaviour.fire () (at Assets/Scripts/ShotgunBehaviour.cs:35)
    ShotgunBehaviour.Update () (at Assets/Scripts/ShotgunBehaviour.cs:28)

    Here's the SG script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ShotgunBehaviour : MonoBehaviour
    6. {
    7.     public int pelletCount;
    8.     public float spreadAngle;
    9.     public GameObject pellet;
    10.     public float pelletFireVel = 1;
    11.     public Transform BarrelExit;
    12.     List<Quaternion> pellets;
    13.  
    14.     void Awake()
    15.     {
    16.         pellets = new List<Quaternion>(pelletCount);
    17.         for (int i = 0; i < pelletCount; i++)
    18.         {
    19.             pellets.Add(Quaternion.Euler(Vector3.zero));
    20.         }
    21.     }
    22.  
    23.     // Update is called once per frame
    24.     void Update()
    25.     {
    26.         if (Input.GetButtonDown("Fire1"))
    27.         {
    28.             fire();
    29.         }
    30.     }
    31.  
    32.     void fire()
    33.     {
    34.         int i = 0;
    35.         foreach(Quaternion quat in pellets)
    36.         {
    37.             pellets[i] = Random.rotation;
    38.             GameObject p = Instantiate(pellet, BarrelExit.position, BarrelExit.rotation);
    39.             p.transform.rotation = Quaternion.RotateTowards(p.transform.rotation, pellets[i], spreadAngle);
    40.             p.GetComponent<Rigidbody>().AddForce(p.transform.right * -pelletFireVel);
    41.             i++;
    42.         }
    43.     }
    44. }
    45.  
     
  2. Lekret

    Lekret

    Joined:
    Sep 10, 2020
    Posts:
    359
    You can't modify collection during foreach, and you are doing exactly that. Enumerating through pellets and changing its content in 37 line. If you want to modify something, then use for cycle.
     
  3. squirrelnuts987

    squirrelnuts987

    Joined:
    Feb 9, 2021
    Posts:
    4
    Thank you for your answer, but could you explain how I could do that?
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    Cheesiest easiest way: capture the list into an array.

    Turn this line:

    Code (csharp):
    1. foreach(Quaternion quat in pellets)
    into this:

    Code (csharp):
    1. foreach(Quaternion quat in pellets.ToArray())
    Hundreds of other approaches too, some far better, all more complicated. If you don't understand the origins or reason you're seeing the problem, best to stick with cheesy-easy.
     
    siddharthsinh and Ahmetefe00 like this.
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,011
    What on earth is the point of that pellet list in the first place? You don't use the values stored in that list anywhere. Instead each time you iterate through your list you just set the quaternion value of each item in a overly complicated fashion. However your code only ever applies the currently created random rotation to the instantiated object. What is that list good for?

    You get the exact same behaviour with this code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class ShotgunBehaviour : MonoBehaviour
    5. {
    6.     public int pelletCount;
    7.     public float spreadAngle;
    8.     public GameObject pellet;
    9.     public float pelletFireVel = 1;
    10.     public Transform BarrelExit;
    11.    
    12.     // Update is called once per frame
    13.     void Update()
    14.     {
    15.         if (Input.GetButtonDown("Fire1"))
    16.         {
    17.             fire();
    18.         }
    19.     }
    20.     void fire()
    21.     {
    22.         for (int i = 0; i < pelletCount); i++)
    23.         {
    24.             var rot = Quaternion.RotateTowards(BarrelExit.rotation, Random.rotation, spreadAngle);
    25.             GameObject p = Instantiate(pellet, BarrelExit.position, rot);
    26.             p.GetComponent<Rigidbody>().AddForce(-p.transform.right * pelletFireVel);        }
    27.         }
    28.     }
    29. }
    Note: Instead of using AddForce you probably just want to set the velocity. AddForce (with ForceMode.Force which is the default) is supposed to be called every physics frame over a period of time. For one-time impulses you should either use the force mode Impulse or VelocityChange.