Search Unity

Deleting all chidlren of an object

Discussion in 'Scripting' started by EndlesNameles, Jun 12, 2011.

  1. h4ph4z4rd

    h4ph4z4rd

    Joined:
    Jan 7, 2015
    Posts:
    1
    I was implementing this in an editor extension, and indeed it required DestroyImmediate, so I used the backward loop variant. Thanks!
     
  2. pointcache

    pointcache

    Joined:
    Sep 22, 2012
    Posts:
    579
    You have to rework this. It is anectodal you dont have normal working GetAllChildren, i cant even find a way to politely express how i feel about it.
     
  3. bustee

    bustee

    Joined:
    Jun 10, 2015
    Posts:
    11
    This might be an issue with the new unity version, but now this will destroy the first child several times (i,e, the list does not get shortened between the calls). So instead of your line

    Code (CSharp):
    1. GameObject.Destroy(root.GetChild(0).gameObject);
    you have to use

    Code (CSharp):
    1. GameObject.Destroy(root.GetChild(i).gameObject);
     
  4. kungfooman

    kungfooman

    Joined:
    Jun 3, 2014
    Posts:
    7
    This freezes and crashes Unity 4.7.1 with more than one child.
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    It probably would form an infinite loop. Destroyed objects don't actually get removed until the end of the frame. Why not just use a simple for loop?
     
  6. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Looks like over the years a few people may have been overlooking the correct answers earlier in the thread.

    This post has the correct way to handle this in-game:
    http://forum.unity3d.com/threads/deleting-all-chidlren-of-an-object.92827/#post-2058407

    Eric explains why it isn't a problem:
    http://forum.unity3d.com/threads/deleting-all-chidlren-of-an-object.92827/#post-2097046

    And the OP needed to use DestroyImmediate since he's writing something that runs in the editor... which is how I ended up here. :)
     
    MechEthan likes this.
  7. Olly

    Olly

    Joined:
    Jun 19, 2014
    Posts:
    20
    I created this; nice n easy.

    Code (csharp):
    1.  
    2. namespace UnityEngine {
    3.     public static class ExtentionMethod {
    4.  
    5.         public static void DestroyAllChildenImmediate(this GameObject parent, MonoBehaviour MonoBehaviour) {
    6.             var children = new List<GameObject>();
    7.             foreach (Transform child in parent.transform) children.Add(child.gameObject);
    8.             children.ForEach(child => MonoBehaviour.DestroyImmediate(child));
    9.         }
    10.  
    11.         public static void DestroyAllChilden(this GameObject parent, MonoBehaviour MonoBehaviour) {
    12.             var children = new List<GameObject>();
    13.             foreach (Transform child in parent.transform) children.Add(child.gameObject);
    14.             children.ForEach(child => MonoBehaviour.Destroy(child));
    15.         }
    16.        
    17.     }
    18. }
    19.  
     
  8. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Code (CSharp):
    1. public static class TransformExtensions
    2. {
    3.     public static void DestroyChildren(this Transform parent)
    4.     {
    5.         foreach (Transform child in parent)
    6.         {
    7.             Object.Destroy(child.gameObject);
    8.         }
    9.     }
    10.  
    11.     public static void DestroyChildrenImmediate(this Transform parent)
    12.     {
    13.         while (parent.childCount > 0)
    14.         {
    15.             Object.DestroyImmediate(parent.GetChild(0).gameObject);
    16.         }
    17.     }
    18. }
     
  9. rsr137

    rsr137

    Joined:
    Oct 7, 2014
    Posts:
    6
    Code (CSharp):
    1. foreach (var t in ParentObject.transform){
    2.             Destroy(((Transform)t).gameObject);
    3. }
    This works for me.
     
  10. treehuggernicky

    treehuggernicky

    Joined:
    Mar 13, 2017
    Posts:
    1
    This code should be:
    Code (csharp):
    1.             int childs = transform,.childCount;
    2.             for (int i = childs - 1; i >= 0; i--)
    3.             {
    4.                 GameObject.Destroy(transform.GetChild(i).gameObject);
    5.             }
    All that needed to be added was the =
     
  11. letsplaysoft16

    letsplaysoft16

    Joined:
    Apr 6, 2017
    Posts:
    1
    Hi,
    this code works

    while (transform.childCount > 0)
    {
    GameObject.DestroyImmediate(transform.GetChild (0).gameObject);
    }

    Using DestroyImmediate the childcount is updated in the current frame.
     
    gwendalbroudin likes this.
  12. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    TL;DR: the 5 most recent posts above are badly wrong and should NOT be used

    Today I discovered a bug deep in a Unity project. The standard pattern is:

    "foreach( child in thing.transform ) ... Destroy( child.gameObject)"

    This doesn't actually work, has never worked in Unity. It has a 1 frame delay during which time Unity deliberately corrupts your data structures (lots of dangling, fake-null, pointers sitting there). You can have code in your game that lives for months - or even years! - that you don't notice is incorrect for a single frame (because that's 0.1 seconds!).

    ...but then one day you hit a situation where it has to be correct, and your code will explode. So, don't do that.

    As described on previos page (and completely ignored by the last 2 years of posters, sigh), to maintain consistency in general you need an extra line at the end:

    https://docs.unity3d.com/ScriptReference/Transform.DetachChildren.html

    ...which doesn't matter in 90% of cases. But that 10%? It matters. So put it in always, and your code will be correct (instead of hiding a bug for Future-You to find)
     
  13. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Oh don't be so dramatic :D

    https://docs.unity3d.com/ScriptReference/Object.Destroy.html
    And the "fake" null thing has been addressed over and over again. Unity overloaded the equality operators. My guess is that if they could go back in time and not do that then they probably would but it would be a very breaking change to do so now.

    If you need to reference stuff that can potentially be destroyed during gameplay then null check it first.
     
  14. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Your data structures are incorrect during that time unless code specifically is rewritten from standard C# into "unity's proprietary definition of C#".

    If you use 3rd party code, libraries, open-source C# ... or re-use code between other C# platforms and Unity .. this is likely to become an issue sooner or later. If you only make small/simple projects, and don't interact with other teams, then - sure, you may never see the problems.

    Why write code that is definitely wrong when you can avoid that by writing correctly?

    NB: what you call "dramatic" is something I've seen cost $000's in contractor fees to diagnose and fix problems caused by minor mistakes like this - usually this kind of thing is buried somewhere deep in code that hasn't been edited in months or years, and it takes a lot of work to track down the actual problem. Shrug. YMMV.
     
  15. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Hm, it has always worked. This code marks all the children of the Thing for "destruction" at the end of the current frame.

    All the confusion people had in this thread comes from not realizing or forgetting what Destroy() method does and what "destruction" of an object derived from Unity.Object actually means.

    foo.Destroy() marks the object for "destruction" that will happen at the end of the frame. DisposeAsync would be a more descriptive name.

    "Destruction" means releasing all the Unity's native resources associated with the managed wrapper object that this variable foo references. Everything that lives on our managed side of the world stays intact.

    Unity doesn't magically corrupt our data structures. All our managed data is still there. We can attach a custom component it to a game object, then "destroy" the component, "destroy" the whole object and still be able to read and write our data through the reference to this component. No one can actually remove managed data from memory except the Garbage Collector, and the GC won't do that while we still have references to this data.

    The way Unity exploits the comparison override is quite unusual, but still completely valid from the C#'s point of view. It's a regular C#.
     
    KelsoMRK likes this.
  16. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    You're using third party libraries and cross-platform C# code that manipulates derivations of UnityEngine.Object? Care to explain how that's possible? Because that's the only scenario we're talking about here.
     
  17. So someone didn't bother to learn how the tool work he/she uses, made mistakes, cost a couple thousands and you blame the tool. Seems legit.

    ROFL.

    Good luck with your future ramblings.
     
  18. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    I either didn't see this or you added it later.

    I'm sorry but if your organization spent hundreds of thousands of dollars to track down a null reference exception then the leadership in that organization has absolutely no idea what it's doing. Especially given that this is a very well-known thing for anyone who has spent any reasonable amount of time doing development with these tools; or any development that involves communicating back and forth between managed and unmanaged code.

    Frankly - it sounds like you're salty because you found a bad bug in an older part of your code that resulted because you didn't fully understand how object destruction works in Unity. Now you know how it works and will be less likely to make that same mistake in the future. You learned a thing! You're a better software developer! Go forth and make cool stuff!
     
  19. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Guys ...

    1. Not my code. Code I was asked to help fix.

    2. C# has lifecycle rules, Unity knowingly breaks them. This is not a matter of debate. There are good and bad reasons for why Unity did what they did 10+ years ago.

    3. It is an extremely common pattern to want to destroy (small D) objects in a managed language; Unity's obscure and crappy support for that causes a recurring problem of people doing what seems right and yet it's only 99% correct.

    4. MANY PEOPLE in this thread had explained the correct way to do this, and then 3 posters in a row posted, essentially - "I have no idea what's going on here, I don't udnerstnad the problems, but I found I could ignore the correct answer and do somethign naive that I don't understand, and will ruin people's code in future". I was trying to correct that!
     
  20. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    124
    after reading most of this thread i got confused. Assuming the best method is:
    Code (CSharp):
    1.  
    2.    int childs = transform.childCount;
    3.         for (int i = childs - 1; i >= 0; i--)
    4.         {
    5.             GameObject.Destroy(transform.GetChild(i).gameObject);
    6.         }
    7.  
    please correct me if I'm wrong
     
  21. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,443
    You're not wrong. But you don't need to do any weird counting-backwards or making-lists things. The Destroy function just marks objects for destruction at the end of the frame. Maybe way back in the ancient days this was necessary.

    Code (CSharp):
    1. foreach (Transform child in parentObj.transform)
    2.     Destroy(child.gameObject);
    3.  
     
  22. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    There was a point when this code would destroy every second GameObject. Dunno if the underlying implementation has changed. I just got so used to doing it backwards that this is just my normal habit now.

    I'm still leery of removing objects from a collection during foreach.
     
  23. Vivien_Lynn

    Vivien_Lynn

    Joined:
    May 17, 2019
    Posts:
    19
    It seems to me that iterating backwards through the children is the best way of doing this. Mainly because you don't allocate memory for the Collection of children.

    To quote Unitys documentation:
    "Also note that you should never iterate through arrays and destroy the elements you are iterating over. This will cause serious problems (as a general programming practice, not just in Unity)."
    - Unity Documentation, Object.DestroyImmediate()
    Disclaimer: Do NOT use DestroyImmediate() for your game code. This is for meant for Editor Code!
     
  24. VodaLechebna

    VodaLechebna

    Joined:
    Dec 29, 2020
    Posts:
    4
    The only true way
    Code (CSharp):
    1.     public static class TransformExtensions
    2.     {
    3.         public static void DestroyAllChildren(this Transform objTransform)
    4.         {
    5.             for (var i = objTransform.childCount - 1; i >= 0; --i) {
    6.                 Object.Destroy(objTransform.GetChild(i).gameObject);
    7.             }
    8.             objTransform.DetachChildren();
    9.         }
    10.     }