Search Unity

How to remove GameObjects from list? A bit confusing

Discussion in 'Scripting' started by zyonneo, Sep 6, 2019.

  1. zyonneo

    zyonneo

    Joined:
    Apr 13, 2018
    Posts:
    386
    When I click LoadButton I have loaded certain gameobjects into the scene and added them to a list called allreadyLoaded.So next time when I click the load button the currently load items must clear the list and also get removed from the scene.The solutions I tried are as follows

    1) I used the below code at the start when calling the Load function.
    Code (CSharp):
    1. allreadyLoaded.Clear();
    Problem - The list gets cleared but loaded items are still on the scene and come along with newly loaded GameObjects.

    2)Next code I tried is but got ArgumentOutOfRangeException error
    Code (CSharp):
    1.  
    2.         if(allreadyLoaded.Count!=0)
    3.         {          
    4.             int tc = allreadyLoaded.Count;
    5.          
    6.             for (int i = 0; i < tc; i++)
    7.             {
    8.                 Destroy(allreadyLoaded[i]);
    9.                  allreadyLoaded.RemoveAt(i);
    10.          
    11.             }
    12.        
    13.         }
    I also tried with the following code, first removing the list and then deleting the Gameobject.
    Code (CSharp):
    1.  
    2. allreadyLoaded.RemoveAt(i);
    3. Destroy(allreadyLoaded[i]);
    4.  
    3)Finally I got the solution by using the below code
    Code (CSharp):
    1.  
    2.         if(allreadyLoaded.Count!=0)
    3.         {          
    4.             int tc = allreadyLoaded.Count;          
    5.             for (int i = 0; i < tc; i++)
    6.             {              
    7.                 Destroy(allreadyLoaded[i]);                        
    8.             }
    9.             allreadyLoaded.Clear();
    10.         }
    After destroying I am clearing the List.I think there is alternate way to delete and remove inside the For Loop.
    Any Explanation?
     
  2. thomson9292

    thomson9292

    Joined:
    Aug 26, 2019
    Posts:
    17
    I would use foreach for this. Not sure if there is an simpler way.

    Code (CSharp):
    1.         foreach(GameObject u in Units) {
    2.             Destroy(u);
    3.         }
    4.         Units.Clear();
    Edit.
    List only keeps reference to the GameObject so clearing List is one thing and destroying gameObject you have to handle separately.
     
    Last edited: Sep 6, 2019
  3. zyonneo

    zyonneo

    Joined:
    Apr 13, 2018
    Posts:
    386
    Yea that exact thing I done using for loop.
     
  4. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    RemoveAt does what it says and thus messes up indices of the following objects and count. If you want to use RemoveAt one usually starts at the end of the list and goes backwards:
    Code (csharp):
    1.  
    2. for ( int i = tc - 1; i > -1; --i)
    3. {
    4.    Destroy(allreadyLoaded[i]);
    5.    allreadyLoaded.RemoveAt(i);
    6. }
    7. allreadyLoaded.Clear();
    8.  
    Note that Destroy only flags the GameObject for termination at the end of the frame and does not work immediately. There is DestroyImmediate but it is advised not to use it for various reasons.
    When you repeatedly create and destroy GameObjects you should consider pooling them. This keeps them deactivated and just places them when needed. Takes some burden from memory management.

    Note its "already" with one L!
     
    zyonneo likes this.
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I like to just do something like this, since I find list.ToArray super handy for iterating over a list I'm modifying. I haven't done performance comparisons vs already mentioned solutions.

    Code (csharp):
    1. foreach (GameObject g in alreadyLoaded.ToArray())
    2. {
    3.     Destroy(g);
    4.     alreadyLoaded.Remove(g);
    5. }
     
    Ken_Rampage, zyonneo and Munchy2007 like this.
  6. sstrong

    sstrong

    Joined:
    Oct 16, 2013
    Posts:
    2,255
    Generally, if you're modifying a list use for(,,) rather than a foreach loop. With your sample you are (probably) calling ToArray each iteration. If you have a list, just remove the items directly from the list as mentioned by @exiguous above. You can use Unity Profiler to figure out what's happening. Converting to an array (in a loop) then modifying the list is unlikely to bring any benefits. I hope that makes some sense.
     
    Joe-Censored likes this.
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I'm sure ToArray being called on each iteration is not the case, as the array iterated over doesn't reflect changes to the list made within the loop (the whole point of using ToArray). The for(,,) solution against the original list is good of course, but lacks some flexibility, in that you're forced to iterate over the list backwards. It is not unusual to need to process objects in a collection in the order they were added for example.

    You could of course reverse the list prior to entering the loop, then reverse it again when done, but I'd be surprised if that were much faster than just making the array copy of the list. I've been surprised many times before though :p
     
  8. Ken_Rampage

    Ken_Rampage

    Joined:
    Aug 17, 2020
    Posts:
    2
    This turned out to be a really elegant solution to my problem.

    I was attempting to use a list to track the objects my collider was colliding with then call an interface on those objects to destroy them. Removing the destroyed item from my list in the middle of the foreach loop was giving me enumeration errors.

    Running the foreach loop on my list as if it were an array worked like a charm. Thanks!
     
    Joe-Censored likes this.
  9. snacksTheThoom

    snacksTheThoom

    Joined:
    Sep 15, 2021
    Posts:
    17
    Worked Great for what I needed. Thanks for the suggestion