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
     
    Essential likes this.
  3. bernardfrancois

    bernardfrancois

    Joined:
    Oct 29, 2009
    Posts:
    370
    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...
     
    Brian-Crandell likes 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.     }
     
    IvanRoyLoewen likes this.
  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
    Goyoman, rsodre, Ruud3DV and 2 others like this.
  6. Cheburek

    Cheburek

    Joined:
    Jan 30, 2012
    Posts:
    382
    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
  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:
    734
    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
  10. pablo1517

    pablo1517

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

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    734
    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.
     
  12. 10FingerArmy

    10FingerArmy

    Joined:
    Oct 10, 2013
    Posts:
    225
    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);
     
    Pawl likes this.
  13. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    734
    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:
    442
    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:
    734
    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), 10FingerArmy is also incorrect about it being the shortest or 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: May 14, 2014
    chrismarch likes this.
  16. 10FingerArmy

    10FingerArmy

    Joined:
    Oct 10, 2013
    Posts:
    225
    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:
    734
    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:
    733
    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.
     
    Nition likes this.
  19. Nition

    Nition

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

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    733
    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:
    2,012
    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
     
  24. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    40
    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"));
     
    theFireman, halley, Seanba and 2 others like this.
  26. hydrix

    hydrix

    Joined:
    Apr 30, 2016
    Posts:
    9
    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:
    40
  29. Thom_Denick

    Thom_Denick

    Joined:
    May 19, 2014
    Posts:
    5
    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:
    411
    @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:
    79
    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.    }
     
  32. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    734
    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:
    12
    How is this functionality not in unity already o_O