Search Unity

Change GameObject layer at run time won't apply to child

Discussion in 'Editor & General Support' started by schchang, Apr 11, 2008.

  1. schchang

    schchang

    Joined:
    Apr 11, 2008
    Posts:
    5
    I need to change my game object's layer in the script, but it only apply to my game object's top level where the animation and script are attached. The level where the mesh and render are attached is not changed.

    I noticed that GameObject has a function function to Activate object recursively( SetActiveRecursively() ), should there be a function call SetLayerRecursively() also?

    If not, how can I set layer to a GameObject including all it's children in the script?

    Thanks.
     
  2. Factoid

    Factoid

    Joined:
    Mar 30, 2008
    Posts:
    69
    Code (csharp):
    1.  
    2. function SetLayerRecursively( obj : GameObject, newLayer : int  )
    3. {
    4.     obj.layer = newLayer;
    5.    
    6.     for( var child : Transform in obj.transform )
    7.     {
    8.         SetLayerRecursively( child.gameObject, newLayer );
    9.     }
    10. }
    11.  
    This should work, though recursive functions might not be ideal for deep trees. However, this code can be easily refactored into a non-recursive breadth-first search using the Array class.

    EDIT: child in SetLayerRecursively is now child.gameObject
     
  3. bernardfrancois

    bernardfrancois

    Joined:
    Oct 29, 2009
    Posts:
    373
    I think this should be present in the Unity API. After all, if you change the layer in the editor, there is an option to apply it for all children...
     
    trombonaut, Nodrap, sean244 and 2 others like this.
  4. tgraupmann

    tgraupmann

    Joined:
    Sep 14, 2007
    Posts:
    828
    +1

    Code (csharp):
    1. void SetLayerRecursively(GameObject obj, int newLayer)
    2.     {
    3.         if (null == obj)
    4.         {
    5.             return;
    6.         }
    7.        
    8.         obj.layer = newLayer;
    9.        
    10.         foreach (Transform child in obj.transform)
    11.         {
    12.             if (null == child)
    13.             {
    14.                 continue;
    15.             }
    16.             SetLayerRecursively(child.gameObject, newLayer);
    17.         }
    18.     }
     
  5. andresp

    andresp

    Joined:
    Aug 12, 2011
    Posts:
    38
    +1

    also, why not just:
    Code (csharp):
    1.     public static void SetLayerRecursively(GameObject go, int layerNumber)
    2.     {
    3.         foreach (Transform trans in go.GetComponentsInChildren<Transform>(true))
    4.         {
    5.             trans.gameObject.layer = layerNumber;
    6.         }
    7.     }
    ?
     
    Last edited: Oct 19, 2012
  6. Cheburek

    Cheburek

    Joined:
    Jan 30, 2012
    Posts:
    384
    thats not recursive function
     
  7. andresp

    andresp

    Joined:
    Aug 12, 2011
    Posts:
    38
    I thought the "Resursively" in the name of the function was referring to the effects of the function, i.e. propagating the change to the childs (what is actually asked in this thread) and not its internals.

    Also, even though it is very unlikely to occur in this case, using recursion in such a way (other functions posted in this thread) is asking for stack overflow.

    Additionally, go.GetComponentsInChildren<Transform>(true) seems to iterate in a similar way (DFS).
     
    Last edited: Oct 30, 2012
    Nemikolh and sand_lantern like this.
  8. Lypheus

    Lypheus

    Joined:
    Apr 16, 2010
    Posts:
    664
    Actually no on both accounts.

    Firstly, "recursive" means to perform an operation on an object and each of its children until you reach all nodes which have no children. In your case, you're simply going one level down. In fact, your approach not only does not set layers for children past the first depth, but it also fails to set the root node itself.

    Second, the only way to get a stack overflow in this case is to have an outrageously huge tree (the size of which I doubt Unity3D would support) or to have a node in your hierarchy whose child links back further up the tree (infinite recursion). If you want to engineer a solution to avoid this, you can simply store a "visited node stack" which is checked before entering the recursive call for that node and skipped if it already exists.
     
  9. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    Lypheus, that's incorrect. Andresp's code does everything required - it sets the layers "past the first depth" (grandchildren etc) and does also set the root. GetComponentsInChildren "Returns all components of Type type in the GameObject or any of its children." i.e. it includes the root. It also returns grandchildren etc even though the docs don't appear to say so. Yes, it's poorly named! But it is recursive.
     
    Last edited: Oct 23, 2014
    Novack, avidwriter, noio and 5 others like this.
  10. pablo1517

    pablo1517

    Joined:
    Mar 18, 2014
    Posts:
    3
    Dude, just don't pass null.
     
    encryptX3 likes this.
  11. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    If you did want a null check, I'd still recommend Andresp's version. Just add a null check to the start.

    Code (csharp):
    1.  
    2.  public static void SetLayerRecursively(GameObject go, int layerNumber) {
    3.         if (go == null) return;
    4.         foreach (Transform trans in go.GetComponentsInChildren<Transform>(true)) {
    5.             trans.gameObject.layer = layerNumber;
    6.         }
    7.     }
    8.  
    Simpler than the others, uses built-in functionality, same result.
     
    ricmage likes this.
  12. ImpossibleRobert

    ImpossibleRobert

    Joined:
    Oct 10, 2013
    Posts:
    527
    I think the most efficient and also the shortest way is to write:

    Code (csharp):
    1.  
    2.     public static void SetLayerRecursively(this GameObject obj, int layer) {
    3.         obj.layer = layer;
    4.  
    5.         foreach (Transform child in obj.transform) {
    6.             child.gameObject.SetLayerRecursively(layer);
    7.         }
    8.     }

    This will not cause issues for very large numbers of children as GetComponentsInChildren potentially does and it does not require any null checks. Simply call myGameObject.SetLayerRecursively(13);
     
  13. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    N̶i̶c̶e̶,̶ ̶I̶ ̶d̶i̶d̶n̶'̶t̶ ̶k̶n̶o̶w̶ ̶a̶b̶o̶u̶t̶ ̶S̶e̶t̶L̶a̶y̶e̶r̶R̶e̶c̶u̶r̶s̶i̶v̶e̶l̶y̶.̶

    Edit: Oops, I missed that it was just an extension method. See my post below instead.
     
    Last edited: May 14, 2014
  14. Venryx

    Venryx

    Joined:
    Sep 25, 2012
    Posts:
    444
    What do you mean? He added the method himself. (it's a custom GameObject extension method that he's using in line 5--the method's merely calling itself recursively)
     
  15. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    Oops, I missed that. In that case I would still highly recommend using the Unity internal method. Even ignoring the risk of stackoverflow with the recursive option (since in reality it's negligible), rwetzold is also incorrect about it being the shortest and most efficient.

    A test:

    Code (csharp):
    1.  
    2. void Start () {
    3.     GameObject root = CreateHeirarchy();
    4.     float startTime = Time.realtimeSinceStartup;
    5.     SetLayerOnAll(root, 5);
    6.     float totalTimeMs = (Time.realtimeSinceStartup - startTime) * 1000;
    7.     print("Set layer on all time: " + totalTimeMs + "ms");
    8.     startTime = Time.realtimeSinceStartup;
    9.     SetLayerOnAllRecursive(root, 4);
    10.     totalTimeMs = (Time.realtimeSinceStartup - startTime) * 1000;
    11.     print("Set layer on all recursive time: " + totalTimeMs + "ms");
    12. }
    13.  
    14. static GameObject CreateHeirarchy() {
    15.     GameObject root = new GameObject();
    16.  
    17.     GameObject[] children = new GameObject[100];
    18.     for (int i = 0; i < 100; i++) {
    19.         GameObject child = new GameObject();
    20.         child.transform.parent = root.transform;
    21.         children[i] = child;
    22.     }
    23.  
    24.     GameObject[] grandchildren = new GameObject[1000];
    25.     for (int i = 0; i < 1000; i++) {
    26.         GameObject grandchild = new GameObject();
    27.         grandchild.transform.parent = children[Random.Range(0, 99)].transform;
    28.         grandchildren[i] = grandchild;
    29.     }
    30.  
    31.  
    32.     for (int i = 0; i < 10000; i++) {
    33.         GameObject greatgrandchild = new GameObject();
    34.         greatgrandchild.transform.parent = grandchildren[Random.Range(0, 999)].transform;
    35.     }
    36.  
    37.     return root;
    38. }
    39.  
    40. static void SetLayerOnAll(GameObject obj, int layer) {
    41.     foreach (Transform trans in obj.GetComponentsInChildren<Transform>(true)) {
    42.         trans.gameObject.layer = layer;
    43.     }
    44. }
    45.  
    46. static void SetLayerOnAllRecursive(GameObject obj, int layer) {
    47.     obj.layer = layer;
    48.     foreach (Transform child in obj.transform) {
    49.         SetLayerOnAllRecursive(child.gameObject, layer);
    50.     }
    51. }
    52.  
    On my machine, the results are:
    Set layer on all time: 8.772254ms
    Set layer on all recursive time: 10.9739ms

    Both fast, but the internal GetComponentsInChildren method is both non-recursive and somewhat faster. Note that contrary to its name, it does also get the root and grandchildren etc.
     
    Last edited: Aug 6, 2019
  16. ImpossibleRobert

    ImpossibleRobert

    Joined:
    Oct 10, 2013
    Posts:
    527
    Interesting comparison and good idea to do so! Do you also have a measurement with the extension method I posted? For me it still feels wrong to allocate this huge list without any need knowing that GC is one of the worst things in Mono. Do you have stats about GC and memory usage?
     
  17. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    Yeah, it takes the same amount of time when set up as an extension method.

    Good question re memory use and the list. I'm not sure. If testing that I recommend adding a bunch of components to the test GameObjects since plain GameObjects don't seem to use much memory at all, but GameObjects with tons of stuff on them probably do.
     
  18. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    In my initial test of a game object with exactly 200 children (of varying depths), SetLayerRecursively generates 6.3KB - 6.7KB, while the GetComponentsInChildren method only generates 4 - 4.3KB. GetComponentsInChildren also operates at ~.83 ms, while SetLayerRecursively operates at ~1ms.

    This is all with Unity's compiler, and SetLayerRecursively is an extension method. In addition, GetComponentsInChildren has the ability to be used across multiple frames, which might be of use if your child hiearchy is unpredictable (for instance, if creating a script that will be used in an Asset Store package) or just plain monstrous.
     
    Novack and Nition like this.
  19. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    Sweet. Maybe we can finally lay all these custom propositions to rest.
     
  20. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    After some further testing, I've found that there is some base overhead of the GetComponentsInChildren method in terms of speed. For example, using this method on a game object with no children takes a whopping .6 ms, while the recursive version only takes .3 ms.

    So, for smaller hierarchies, it definitely makes more sense to use the recursive method. I am not sure where the cutoff is where GetComponentsInChildren starts being more efficient (around 100 children maybe?), although it does appear to be lower in terms of garbage efficiency.

    Also, keep in my mind my test are not high quality. I am basically just pressing a key and calling either of the two methods in Update and measuring the time/GC alloc in the profiler.
     
  21. DanielDollerup

    DanielDollerup

    Joined:
    Sep 18, 2013
    Posts:
    8
    Code (CSharp):
    1.     /// <summary>
    2.     /// Recursively sets GameObject's layer to newLayer
    3.     /// </summary>
    4.     /// <param name="newLayer">The new layer</param>
    5.     /// <param name="trans">Start transform</param>
    6.     void SetLayer(int newLayer, Transform trans)
    7.     {
    8.         trans.gameObject.layer = newLayer;
    9.         foreach (Transform child in trans)
    10.         {
    11.             child.gameObject.layer = newLayer;
    12.             if (child.childCount > 0)
    13.             {
    14.                 SetLayer(newLayer, child.transform);
    15.             }
    16.         }
    17.     }
    Is what I got.. if it helps
     
  22. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    Everyone is clearly itching for a software challenge.
     
  23. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,324
    Code (CSharp):
    1.     public static void SetLayer(Transform root, int layer)
    2.     {
    3.         Stack <Transform> children = new Stack<Transform> ();
    4.         children.Push (root);
    5.         while (children.Count > 0) {
    6.             Transform currentTransform = children.Pop ();
    7.             currentTransform.gameObject.layer = layer;
    8.             foreach (Transform child in currentTransform)
    9.                 children.Push (child);
    10.         }
    11.     }
    I win
     
    brainwipe likes this.
  24. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    77
    Made it into an extension of GameObject

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public static class GameObjectExtension {
    6.  
    7.     public static void SetLayer(this GameObject parent, int layer, bool includeChildren = true)
    8.     {
    9.         parent.layer = layer;
    10.         if (includeChildren)
    11.         {
    12.             foreach (Transform trans in parent.transform.GetComponentsInChildren<Transform>(true))
    13.             {
    14.                 trans.gameObject.layer = layer;
    15.             }
    16.         }
    17.     }
    18. }
    19.  
    which means you can do:

    Code (CSharp):
    1. void SomeFunction()
    2. {
    3.   gameObject.SetLayer(0);   // will do it to all children, including inactive
    4.   gameObject.SetLayer(0, false);   // will just do it to the main object
    5. }
    6.  
     
  25. Eliot_L

    Eliot_L

    Joined:
    Jan 10, 2013
    Posts:
    4
    And here's a generic extension method that can run any operation on children recursively:
    Code (csharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public static class UnityExtensions
    5. {
    6.     public static void RunOnChildrenRecursive(this GameObject go, Action<GameObject> action) {
    7.         if (go == null) return;
    8.         foreach (var trans in go.GetComponentsInChildren<Transform>(true)) {
    9.             action(trans.gameObject);
    10.         }
    11.     }
    12. }
    Which would allow you to do things like:
    Code (csharp):
    1. someObject.RunOnChildrenRecursive(child => child.layer = LayerMask.NameToLayer("UI"));
     
  26. hydrix

    hydrix

    Joined:
    Apr 30, 2016
    Posts:
    10
    sorry to necro post but this was the first results in google

    here's a one liner, which might be a little slower but looks nice

    Code (CSharp):
    1. Array.ForEach(gameObject.GetComponentsInChildren<Transform>(), (entry) => entry.gameObject.layer = LayerMask.NameToLayer("MyLayer"));
    if you wanted to go for speed, I would probably just use a function with a recursive loop like above using for (not foreach)
     
  27. ridhima1005

    ridhima1005

    Joined:
    Sep 25, 2018
    Posts:
    2
    Can I know how did you set a layer to a GameObject during runtime via script? The GameObject is generated only during the runtime. GameObjects are the walls built in Mapbox
     
  28. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    77
  29. Thom_Denick

    Thom_Denick

    Joined:
    May 19, 2014
    Posts:
    15
    I think you're a bit confused. The question is about assigning the layer to all children in a root object. Your example only sets it in the top hierarchy (and the one below.) Indeed, it needs to be done recursively, as in for all gameobjects in the hierarchy.

    If you're not sure what I'm talking about, here's an example of getting a folder hierarchy recursively in the same way you need to do it for GOs with extended hierarchies here:
    https://www.dotnetperls.com/recursive-file-list
     
  30. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    @Thom_Denick thanks, you could be right, and I can't be bothered looking in to it just now - so I deleted that post!

    {I think I copied it across from a project incorrectly and forgot to recurse - anyway it's irrelevant.} Thanks cheers
     
    Thom_Denick likes this.
  31. gambr

    gambr

    Joined:
    Oct 13, 2017
    Posts:
    109
    Little modification to recurse all descendants:

    Code (CSharp):
    1.    public static void applyRecursivelyOnDescendants(this GameObject gameObject, System.Action<GameObject> action) {
    2.       if (gameObject != null) {
    3.          action(gameObject);
    4.          foreach (Transform transform in gameObject.transform) {
    5.             transform.gameObject.applyRecursivelyOnDescendants(action);
    6.          }
    7.       }
    8.    }
     
    JoRangers likes this.
  32. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    GetComponentsInChildren already includes all descendants (and the root as well). It doesn't only return immediate children. Your method will have the same result as Eliot_L's method but be a little slower (unless relative performance has changed - it's been a few years now).
     
    Last edited: Mar 3, 2019
  33. albertjames

    albertjames

    Joined:
    Aug 11, 2018
    Posts:
    16
    How is this functionality not in unity already o_O
     
    trombonaut, ModLunar and EyePD like this.
  34. Darjamfar

    Darjamfar

    Joined:
    Jun 4, 2012
    Posts:
    63
    I found the following Unity functionality which seems to work

    Code (CSharp):
    1. //set root layer
    2. myCharacter.layer = layerID;
    3.  
    4. //change all root's children
    5. myCharacter.transform.SetChildLayer(layerID);
    6.  
     
  35. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    467
    @DarJam that doesn't seem to be in the documentation and doesn't work either. Are you sure you don't have an extension somewhere in your code?
     
  36. naviln

    naviln

    Joined:
    Oct 18, 2016
    Posts:
    32
    Awesome, love it, thank you!

    I just made it a extension method on GameObject (just added this):

    public static void SetLayerRecursively(this GameObject go, int layerNumber)
    {
    foreach (Transform trans in go.GetComponentsInChildren<Transform>(true))
    {
    trans.gameObject.layer = layerNumber;
    }
    }


    and calling it now:
    //set it to the layer that only this camera sees and renders
    go.SetLayerRecursively(8);
     
  37. Darjamfar

    Darjamfar

    Joined:
    Jun 4, 2012
    Posts:
    63
    Your right, it's not a method of Transform. Duh! Sorry about that.
    Put this method into a public static class.
    Code (CSharp):
    1.  
    2. public static class Tools
    3. {
    4. public static void SetChildLayers(this Transform t, int layer)
    5. {
    6.    for (int i = 0; i < t.childCount; i++)
    7.    {
    8.       Transform child = t.GetChild(i);
    9.       child.gameObject.layer = layer;
    10.       SetChildLayers(child, layer);
    11.    }
    12. }
    13. .
    14. .
    15. .
    16. //Then you can use it from transform
    17. player.transform.SetChildLayers(layerID);
    18.  
     
    Last edited: Dec 14, 2019
    chadfranklin47 and tjmaul like this.
  38. SkywardRoy

    SkywardRoy

    Joined:
    Jan 14, 2017
    Posts:
    22
    After going through the thread I couldn't help but combine only the parts I liked. This one uses GetComponentsInChildren(typeof(Transform)) instead of GetComponentsInChildren<Transform>() for performance reasons (Source)

    Code (CSharp):
    1. public static void SetLayer (this GameObject gameObject, int layer, bool includeChildren = false) {
    2.     if (!gameObject) return;
    3.     if (!includeChildren) {
    4.         gameObject.layer = layer;
    5.         return;
    6.     }
    7.  
    8.     foreach (var child in gameObject.GetComponentsInChildren(typeof(Transform), true)) {
    9.         child.gameObject.layer = layer;
    10.     }
    11. }
     
    Novack and Bioman75 like this.
  39. zorrendor

    zorrendor

    Joined:
    May 27, 2017
    Posts:
    5
    Foreach loop calls every single iteration gameObject.GetComponentsInChildren(typeof(Transform), true). To make code faster replace with this:

    Code (CSharp):
    1.  
    2.             var children = gameObject.GetComponentsInChildren<Transform>(true);
    3.             for(int i = 0, max = children.Length; i < max; i++)
    4.             {
    5.                 Transform child = children[I];
    6.                 child.gameObject.layer = layer;
    7.             }
    8.  
    [/I]
     
  40. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    77
  41. zorrendor

    zorrendor

    Joined:
    May 27, 2017
    Posts:
    5
    Novack and chadfranklin47 like this.
  42. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    226
    From my short testing session extending the test from @Nition, this is the superior method both in terms of performance and garbage allocation as it is about twice as performant in this scenario and generates 0 garbage. I cleaned (and sped) it up a bit:

    Code (CSharp):
    1. public static void SetLayerRecursively(this Transform parent, int layer)
    2.     {
    3.         parent.gameObject.layer = layer;
    4.  
    5.         for (int i = 0, count = parent.childCount; i < count; i++)
    6.         {
    7.             parent.GetChild(i).SetLayerRecursively(layer);
    8.         }
    9.     }
    On my machine, the results are:
    Set layer on all time: 5.794525ms
    Set layer on all recursive time: 6.498814ms
    Set layer on all recursive transform time (this method): 2.866745ms

    Set layer on all recursive transform time (without setting "count = parent.childCount", and just doing "i < parent.childCount"): 3.996849ms

    Maybe someone can get this even faster :D
     
    Last edited: Aug 9, 2021
  43. Yany

    Yany

    Joined:
    May 24, 2013
    Posts:
    96

    If you are interested, you can test my version (based on yours) too:
    Code (CSharp):
    1.  
    2. public static void SetLayer(this Transform transform, string layer, bool includeChildren = true)
    3. {
    4.     SetLayer(transform, LayerMask.NameToLayer(layer), includeChildren);
    5. }
    6.  
    7. public static void SetLayer(this Transform transform, int layer, bool includeChildren = true)
    8. {
    9.     transform.gameObject.layer = layer;
    10.     if (!includeChildren)
    11.     {
    12.         return;
    13.     }
    14.     for (int i = transform.childCount - 1; i >= 0; i--)
    15.     {
    16.         transform.GetChild(i).SetLayer(layer);
    17.     }
    18. }
    19.  
     
  44. akshaypro02

    akshaypro02

    Joined:
    Jan 14, 2020
    Posts:
    9
    item.transform.GetChild(0).gameObject.layer = 0;
     
  45. tokar_dev

    tokar_dev

    Joined:
    Feb 1, 2017
    Posts:
    9
    I face with necessary switch layers on a Renderer.
    For more efficient I used Generic by <Component>. In case for specific Component GetComponentsInChildren executes faster, then Transform by default.
    Code (CSharp):
    1.  
    2.         /// <summary>
    3.         /// Set layer gameobject.
    4.         /// </summary>
    5.         /// <param name="gameobject">GameObject</param>
    6.         /// <param name="layer">layer num.</param>
    7.         /// <param name="includeChildren">Toggle set for children</param>
    8.         public static void SetLayer<T>
    9.                    (this GameObject gameobject, int layer, bool includeChildren = false)
    10.                     where T : Component
    11.         {
    12.             gameobject.layer = layer;
    13.             if (includeChildren == false) return;
    14.  
    15.             var arr = gameobject.GetComponentsInChildren<T>(true);
    16.             for (int i = 0; i < arr.Length; i++)
    17.                 arr[i].gameObject.layer = layer;
    18.         }
     
  46. Nanachi88

    Nanachi88

    Joined:
    Aug 10, 2022
    Posts:
    14
    Could you please help me with this error?
     

    Attached Files:

  47. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,838
    The example you've quoted is an extension method which requires the method to be a static method, which yours isn't.