Transform.Find(string) no longer finds grandchild?

I have the following hierarchy:

  • Manager
  • Player(clone)
  • Ship(clone)
  • Q_Quad

This hierarchy is created at run time by a manager script on the ‘Manager’ GameObject, like so:

GameObject player, ship;

player = (GameObject) Instantiate(playerPrefab, vector, quat);
ship = (GameObject) Instantiate(shipPrefab, vector, quat);
ship.transform.parent = player.transform;

The ‘Q_Quad’ GameObject is part of the ‘shipPrefab’. The ‘Q_Quad’ GameObject is inactive initially.

I have a ‘PlayerController’ script on the ‘playerPrefab’ which tries to find ‘Q_Quad’, like so:

transform.Find("Q_Quad").gameObject.SetActive(true);

This system was working fine the other day, but now the call to ‘Find(“Q_Quad”)’ is returning null. As a test, after I ran the scene, I manually moved the ‘Q_Quad’ GameObject to be under ‘Player(clone)’, and then executed the code that finds ‘Q_Quad’, and it works. But when it’s under ‘Ship(clone)’ it fails to work.

I don’t think I changed anything, and I know this worked before (I have video proof of it). Does anyone know what is going on here?

I think you confused the static GameObject.Find with Transform.Find. Transform.Find always starts searching on the direct children while GameObject.Find can do different kinds of searching.

You can implement an extension method which searchs for a deep child:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public static class TransformDeepChildExtension
{
    //Breadth-first search
    public static Transform FindDeepChild(this Transform aParent, string aName)
    {
        Queue<Transform> queue = new Queue<Transform>();
        queue.Enqueue(aParent);
        while (queue.Count > 0)
        {
            var c = queue.Dequeue();
            if (c.name == aName)
                return c;
            foreach(Transform t in c)
                queue.Enqueue(t);
        }
        return null;
    }    

	/*
	//Depth-first search
	public static Transform FindDeepChild(this Transform aParent, string aName)
	{
		foreach(Transform child in aParent)
		{
			if(child.name == aName )
				return child;
			var result = child.FindDeepChild(aName);
			if (result != null)
				return result;
		}
		return null;
	}
	*/
}

The first implementation uses a Breadth-first search while the second one (which is commented out) uses a Depth-first search. Usually the first one is the preferred one. The first implementation uses Transform.Find for each level, so you can still use relative path within deepchildren.

edit
Just in case you don’t know what extension methods are: all you have to do is copy this class above somewhere into your project. It doesn’t matter where. On any Transform reference you can simply call:

Transform someChild = transform.FindDeepChild("someChild");

instead of transform.Find

I used GetComponentsInChildren. it’s not super effiecient so it wouldn’t be a great idea to use during your game loop, but I’m only running it at load time. Seems a bit simpler than writing your own recursive search.

Transform[] children = transform.GetComponentsInChildren<Transform> ();
foreach (var child in children) {
	if (child.name == "object name") {
		//do something with child
	}
}

You can find deep child using LINQ:

var childObject = parentObject.GetComponentsInChildren<Transform>()
                            .FirstOrDefault(c => c.gameObject.name == desiredName)?.gameObject;

Unbelievable that Unity doesn’t have something like this built-in!

GetComponentInDescendants please!
And also, please add GetComponentInDescendantsByName(string name)

R.

I found a completely different way of winning here by using the OverlapSphere function with a very short range. I then ask it if any of the colliders hit have the name of the grandchild I am looking for. Here I was looking for the granchild gameobject with the name “head”, that I wanted to put in a GameObject variable called thehead. Copy and paste this into a script attached to the object that you need to find the grandchild of by name:

  void FindChildName(Vector3 center, float radius)
    {
        Collider[] hitColliders = Physics.OverlapSphere(transform.position, 2.5f);
        int i = 0;
        while (i < hitColliders.Length)
        {
            if (hitColliders_.gameObject.name == "head"&& hitColliders*.transform.IsChildOf(transform))*_

{
thehead = hitColliders*.gameObject;*
}
i++;
}
}
Then in Start(), call it with:
FindChildName(transform.position, 2.5f);
It also checks if it is a child of the object by using transform.IsChildOf. You might need to adjust the range depending on the size of your GameObject.

The issue is actually that it’s disabled. I’m pretty sure it will find it when it’s active.