rootpanthera
rootpanthera

Reputation: 2771

Initialize only variable once, while function calls itself multiple times

This function calls itself multiple times - finding a child of a game object and passing that object into itself again.

I want to return a list of game objects that were found while traversing. I can't initialize a list in a function itself because I need to initialize it only once. When calling it multiple times it will re-initialize.

Any idea on how to fetch a list of fetched objects?

public static List<GameObject> FindAllGameObjectsInGameObject<T>(GameObject gameObjectToTraverse)
    {
        List<GameObject> list;
        
        

        foreach (Transform child in gameObjectToTraverse.transform)
        {
            if (list == null)
            {
                list = new List<GameObject>();
            }
            list.Add(child.gameObject);
            FindAllGameObjectsInGameObject<T>(child.gameObject);
        }
       

    }

Upvotes: 0

Views: 413

Answers (1)

derHugo
derHugo

Reputation: 90629

Instead of doing the recursion yourself at all why not simply use GameObject.GetComponentsInChildren

Returns all components of Type type in the GameObject or any of its children.

Unity searches for components recursively on child GameObjects. This means that it also includes all the child GameObjects of the target GameObject, and all subsequent child GameObjects.

and do e.g.

using System.Linq;

and then

public static List<GameObject> FindAllGameObjectsInGameObject(GameObject gameObjectToTraverse)
{
    // This already gives you ALL transform components anywhere nested 
    // under the given object including inactive and disabled ones
    // INCLUDING the given object itself
    var allChildTransforms = gameObjectToTraverse.GetComponentsInChildren<Transform>(true);
    // This uses Linq to rather get all the according GameObjects 
    // See https://learn.microsoft.com/dotnet/api/system.linq.enumerable.select
    var allChildGameObjects = allChildTransforms.Select(t => t.gameObject);
    // Optional if you don't want to return the original given object itself
    // See https://learn.microsoft.com/dotnet/api/system.linq.enumerable.where
    var onlyChildGameObjects = allChildGameObjects.Where(c => c!= gameObjectToTraverse);

    return onlyChildGameObjects.ToList();
}

and forget about recursion since Unity already does it for you ;)


Then actually what is the generic parameter T for? You probably might want to use it in the first call

var allChildComponents = gameObjectToTraverse.GetComponentsInChildren<T>(true);

in order to rather get only objects having a component of type T attached.


Just for actually answering on your attempt: You could use an optional parameter like

public static List<GameObject> FindAllGameObjectsInGameObject(GameObject gameObjectToTraverse, List<GameObject> list = new List<GameObject>())
{     
    foreach (Transform child in gameObjectToTraverse.transform)
    {
        list.Add(child.gameObject);
        FindAllGameObjectsInGameObject(child.gameObject, list);
    }
}

So the first time you call it you just do

var objs = FindAllGameObjectsInGameObject(objectToTraverse);

in that case a new list is automatically created as second parameter. Then it is passed on internally in the recursive calls.

Or you could just obfuscate it a bit and simply split those two:

public static List<GameObject> FindAllGameObjectsInGameObject(GameObject gameObjectToTraverse)
{
    return FindAllGameObjectsInGameObjectInternal(objectToTraverse, new List<GameObject>());
}

private static List<GameObject> FindAllGameObjectsInGameObjectInternal(GameObject gameObjectToTraverse, List<GameObject> list)
{     
    foreach (Transform child in gameObjectToTraverse.transform)
    {
        list.Add(child.gameObject);
        FindAllGameObjectsInGameObjectInternal(child.gameObject, list);
    }
}

However, with the optional parameter solution you give the developer the chance to reuse an already existing list instead of creating a new one every time ;)

Upvotes: 2

Related Questions