- Home /
Possible to make GameObject.GetComponentInChildren() check inactive objects?
I've recently started needing to have multiple game states within my game, and decided to set object hierarchies within the scene inactive / active to manage this state.
I was initially very confused by the fact that GameObject.GetComponentInChildren() doesn't work for components on inactive GameObjects.
Though it is mentioned in the online documentation it's far from obvious - this is the sort of behaviour that should be printed in massive bold letters or under a "please note" heading... (http://docs.unity3d.com/Documentation/ScriptReference/Component.GetComponentInChildren.html)
After a bit of googling / poking about in forums etc. I couldn't find any "standard" way (i.e. out of the box unity function) to do it...
...but I did find enough info to write my own code to do it; and since I had not found any conclusive answers I decided to post a question and then answer it to help anyone else who might be stuck with the same issue :)
Answer by darbotron · Oct 13, 2013 at 08:55 PM
So whilst GetComponentInChildren() doesn't check the components of inactive GameObjects, it turns out that GetComponent() does check the components of an inactive obejct.
The obvious solution to my problem was to write code to traverse a GameObject hierarchy by hand and call GetComponent() manually on each GameObject.
Here's the code:
///////////////////////////////////////////////////////////////
/// <summary>
/// Game object tools.
/// </summary>
///////////////////////////////////////////////////////////////
static public class GameObjectTools
{
///////////////////////////////////////////////////////////
// Essentially a reimplementation of
// GameObject.GetComponentInChildren< T >()
// Major difference is that this DOES NOT skip deactivated
// game objects
///////////////////////////////////////////////////////////
static public TType GetComponentInChildren< TType >( GameObject objRoot ) where TType : Component
{
// if we don't find the component in this object
// recursively iterate children until we do
TType tRetComponent = objRoot.GetComponent< TType >();
if( null == tRetComponent )
{
// transform is what makes the hierarchy of GameObjects, so
// need to access it to iterate children
Transform trnsRoot = objRoot.transform;
int iNumChildren = trnsRoot.childCount;
// could have used foreach(), but it causes GC churn
for( int iChild = 0; iChild < iNumChildren; ++iChild )
{
// recursive call to this function for each child
// break out of the loop and return as soon as we find
// a component of the specified type
tRetComponent = GetComponentInChildren< TType >( trnsRoot.GetChild( iChild ).gameObject );
if( null != tRetComponent )
{
break;
}
}
}
return tRetComponent;
}
}
I've not bothered to do an equivalent for GetComponentsInChildren() but it should be a simple enough extension of the code above.
I'd just like to add another comment here - people have upvoted the below comment, which is fine.
To be 100% clear GetComponentsInChildren() returns an array which is allocated with new and therefore will incur GC cost.
If what you want to do is just get a single component in children then the code above is still a better bet I$$anonymous$$O - certainly I use it myself in preference to GetComponentsInChildren() when appropriate.
That's exactly what I needed ! Thank you for the quality of the code, it's so $$anonymous$$ono/Unity friendly. I've made an extension method out of it.
Answer by NickWu · Mar 28, 2014 at 09:31 AM
actually you may try GetComponentsInChildren(true) https://docs.unity3d.com/Documentation/ScriptReference/Component.GetComponentsInChildren.html there is a boolean parameter allow you to include inactive objects
I'm not sure this bool has always been there, if it has I missed it! the code I posted also works for this BTW :)
notice that the single component method doesn't have the parameter... only the components[] version... the one that returns an array... the one with an S... this one: GetComponentsInChildren
I chose my solution as the answer because this optional flag is only available on GetComponentsInchildren() which returns an array and I wanted a solution that returned just the first component.
Also my solution doesn't cause any allocation AFAI$$anonymous$$ so no associated GC hit, which GetComponentsInchildren() definitely does have...
Answer by canis · Oct 04, 2016 at 06:37 PM
I know it's the old post, but after I saw it, I run some test for myself. and here is my version,
/// <summary>Get Component within child transform (extension)</summary>
/// <typeparam name="T">Component class</typeparam>
/// <param name="component">this</param>
/// <param name="depth">searching depth, stop search when reching Zero.</param>
/// <param name="includeInactive">include inactive gameobject.</param>
/// <param name="includeSelf">include the caller itself.</param>
/// <returns>Return first found component</returns>
/// <remarks>The performance are much slower then the original, avg: 4x ~ 20x depend on how many level needed to drill down.</remarks>
public static T GetComponentInChildren<T>(this Component component, int depth = 1, bool includeInactive = false, bool includeSelf = true) where T : Component
{
T rst = null;
if (includeSelf)
rst = component.GetComponent<T>();
else if(depth < 0)
Debug.LogError("Syntax Error: GetComponentInChildren<" + typeof(T).Name + "> You searching for nothing.", component.gameObject);
if (depth > 0 && rst == null)
{
depth--;
foreach (Transform child in component.transform)
{
if (includeInactive && !child.gameObject.activeSelf)
continue;
rst = child.GetComponentInChildren<T>(depth, includeInactive, true);
if (rst != null)
return rst;
}
}
return rst;
}
funny thing is it's much slower then the original GetComponentInChildren() welcome anyone to improve this.
and here is the test code that I'm using to test the performance.
using UnityEngine;
using Kit.Extend;
public class CaseTest : MonoBehaviour
{
[Range(0, 1)]
public int m_TestCase = 0;
public int m_Depth = 10;
public int m_Cycle = 10;
void OnEnable()
{
System.Diagnostics.Stopwatch clock = new System.Diagnostics.Stopwatch();
clock.Start();
Collider rst = null;
switch (m_TestCase)
{
case 0:
for (int i = 0; i < m_Cycle; i++)
rst = GetComponentInChildren<Collider>(true);
break;
case 1:
for (int i = 0; i < m_Cycle; i++)
rst = this.GetComponentInChildren<Collider>(m_Depth, true, true);
break;
}
clock.Stop();
if (rst == null)
{
Debug.LogWarningFormat("{2} {0}, Time : {1}",
(rst == null ? "-NONE-" : "Found"),
clock.ElapsedTicks,
(m_TestCase == 0 ? "Original" : "extension"));
}
else
{
Debug.LogWarning(string.Format("{2} {0}, Time : {1}",
(rst == null ? "-NONE-" : "Found"),
clock.ElapsedTicks,
(m_TestCase == 0 ? "Original" : "extension")), rst.gameObject);
}
clock.Reset();
}
[ContextMenu("Generate levels with collider")]
public void Generate()
{
int x = 1000;
Transform level = transform;
while(x-- > 0)
{
Transform next = new GameObject("have collider in " + x.ToString()).transform;
next.SetParent(level);
level = next;
}
level.GetOrAddComponent<BoxCollider>();
}
[ContextMenu("Generate some empty levels")]
public void GenerateEmpty()
{
int x = Random.Range(10, 1000);
Transform level = transform;
while (x-- > 0)
{
Transform next = new GameObject("Nothing").transform;
next.SetParent(level);
level = next;
}
}
}
Hey. Thanks for posting. It's always nice to see someone with enough curiosity to fiddle with stuff :)
It's not surprising that your function (and, I assume, the original one I posted) are slower than the stock Unity implementation - we have to use their API to access the objects whereas their code is inside the API; in addition to the recursive function calls it's calling at least one Unity API function on each iteration of the recursion :)
I wouldn't have written it as a recursive function if speed were an issue for my use case - generally (I$$anonymous$$O) you should only be calling functions that traverse the Transform graph (like GetComponentInChildren()) during initialisation code or you're asking for trouble...
I'd be interested to hear how much slower it is as well - in general I'd want to see more like 1000 to 10000 iterations of the loops for meaningful profiling results.