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

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.

function SetLayerRecursively( obj : GameObject, newLayer : int  )
{
	obj.layer = newLayer;
	
	for( var child : Transform in obj.transform )
	{
		SetLayerRecursively( child.gameObject, newLayer );
	}
}

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

5 Likes

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…

6 Likes

+1

void SetLayerRecursively(GameObject obj, int newLayer)
    {
        if (null == obj)
        {
            return;
        }
        
        obj.layer = newLayer;
       
        foreach (Transform child in obj.transform)
        {
            if (null == child)
            {
                continue;
            }
            SetLayerRecursively(child.gameObject, newLayer);
        }
    }
6 Likes

+1

also, why not just:

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

?

13 Likes

thats not recursive function

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(true) seems to iterate in a similar way (DFS).

3 Likes

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.

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.

8 Likes

Dude, just don’t pass null.

1 Like

If you did want a null check, I’d still recommend Andresp’s version. Just add a null check to the start.

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

Simpler than the others, uses built-in functionality, same result.

1 Like

I think the most efficient and also the shortest way is to write:

    public static void SetLayerRecursively(this GameObject obj, int layer) {
        obj.layer = layer;

        foreach (Transform child in obj.transform) {
            child.gameObject.SetLayerRecursively(layer);
        }
    }

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);

7 Likes

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.

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)

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:

void Start () {
    GameObject root = CreateHeirarchy();
    float startTime = Time.realtimeSinceStartup;
    SetLayerOnAll(root, 5);
    float totalTimeMs = (Time.realtimeSinceStartup - startTime) * 1000;
    print("Set layer on all time: " + totalTimeMs + "ms");
    startTime = Time.realtimeSinceStartup;
    SetLayerOnAllRecursive(root, 4);
    totalTimeMs = (Time.realtimeSinceStartup - startTime) * 1000;
    print("Set layer on all recursive time: " + totalTimeMs + "ms");
}

static GameObject CreateHeirarchy() {
    GameObject root = new GameObject();

    GameObject[] children = new GameObject[100];
    for (int i = 0; i < 100; i++) {
        GameObject child = new GameObject();
        child.transform.parent = root.transform;
        children[i] = child;
    }

    GameObject[] grandchildren = new GameObject[1000];
    for (int i = 0; i < 1000; i++) {
        GameObject grandchild = new GameObject();
        grandchild.transform.parent = children[Random.Range(0, 99)].transform;
        grandchildren[i] = grandchild;
    }


    for (int i = 0; i < 10000; i++) {
        GameObject greatgrandchild = new GameObject();
        greatgrandchild.transform.parent = grandchildren[Random.Range(0, 999)].transform;
    }

    return root;
}

static void SetLayerOnAll(GameObject obj, int layer) {
    foreach (Transform trans in obj.GetComponentsInChildren<Transform>(true)) {
        trans.gameObject.layer = layer;
    }
}

static void SetLayerOnAllRecursive(GameObject obj, int layer) {
    obj.layer = layer;
    foreach (Transform child in obj.transform) {
        SetLayerOnAllRecursive(child.gameObject, layer);
    }
}

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.

6 Likes

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?

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.

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.

2 Likes

Sweet. Maybe we can finally lay all these custom propositions to rest.

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.