Search Unity

Dear guy who wrote GetComponentInParent/Children,

Discussion in 'Scripting' started by Zergling103, Apr 28, 2017.

  1. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    392
    This appears to have been a long-standing "weirdness" about the Unity API:

    GetComponentsInParent and GetComponentsInChildren give you the option to IncludeInactive game objects in the search.

    However, the singular, non-garbage-allocating versions GetComponentInChildren and GetComponentInParents do not.

    Why not?

    This has lead to needing to create our own implementations where it iterates through children or parents via the transform, calling GetComponent on each one. This requires doing a bunch of back-and-forth communication between native and managed code and is balls slow.

    Please fix!
     
    Last edited: Apr 28, 2017
    glenneroo and WeltallZero like this.
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You can always use GetComponents* and just take the first result.

    Here's a little wrapper function that should handle this (untested):
    Code (csharp):
    1. public T GetComponentInChildrenPlus<T>(bool includeInactive) where T : Component {
    2. T[] temp = GetComponentsInChildren<T>();
    3. if (temp.Length == 0) return null;
    4. return temp[0];
    5. }
     
  3. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    392
    Alas, it creates garbage. Might be faster than iterating through the hierarchy in managed code, though.
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    Try using the version that takes a list in, and use a recycled list for the job. Avoids garbage that way.

    I have a TempList and TempSet class that I keep around for similar tasks:
    https://github.com/lordofduct/space...master/SpacepuppyBase/Collections/TempList.cs
    https://github.com/lordofduct/space...ter/SpacepuppyBase/Collections/TempHashSet.cs
    https://github.com/lordofduct/space.../SpacepuppyBase/Collections/TempCollection.cs

    Then I do things like:

    Code (csharp):
    1.  
    2. public static T GetComponentInChildren<T>(this GameObject go, bool includeInactive) where T : class
    3. {
    4.     using(var lst = TempCollection.GetList<T>())
    5.     {
    6.         go.GetComponentsInChildren(includeInactive, lst);
    7.         return lst.Count > 0 ? lst[0] : null;
    8.     }
    9. }
    10.  
    I use these temp collections all over the place. Definitely reduces gc a lot. It doesn't completely remove it, because sometimes the collections resize and the old array is tossed out. But it certainly keeps it to a minimum.
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    Although... the api says that GetComponentInChildren (singular) does have what you're asking for:

    https://docs.unity3d.com/ScriptReference/GameObject.GetComponentInChildren.html

    GetComponentInParent doesn't though:
    https://docs.unity3d.com/ScriptReference/GameObject.GetComponentInParent.html

    So to resolve that one... we'd do:
    Code (csharp):
    1.  
    2. public static T GetComponentInParent<T>(this GameObject go, bool includeInactive) where T : class
    3. {
    4.     using(var lst = TempCollection.GetList<T>())
    5.     {
    6.         go.GetComponentsInParent(includeInactive, lst);
    7.         return lst.Count > 0 ? lst[0] : null;
    8.     }
    9. }
    10.  
     
  6. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    Unity is a still a work in progress. There are lots of little inconsistencies.

    For example, the nonAlloc versions of raycasts were only added in version 5.0 (look at the "Other Versions" dropdown in the scripting reference.) Same with the List-returning GetComponent functions. There's no non-alloc GetComponents (yet?) The old way active/inactive worked didn't even inherit inactive from parents - you had to set every individual object inactive (look up SetActiveRecursively.) I think they changed that for 4.0.

    Back when GetComponent was first written they had shortcut functions for them all (transform.renderer, and so on,) meaning there was no obvious way to request inactive ones. You only had to write out GetComponent for scripts.
     
    Kiwasi likes this.
  7. WeltallZero

    WeltallZero

    Joined:
    Oct 23, 2016
    Posts:
    19
    This just tripped me today. Can we please get an update on why GetComponentInParent not only doesn't have an includeInactive flag, but actually does not include inactive gameObjects by default, making it useless in many situations? (if at the very least the behaviour was the opposite, you could get it and then check if the gameObject is active).

    There's no reason people should be forced to implement workarounds for basic functionality.
     
    bluescrn likes this.
  8. Barliesque

    Barliesque

    Joined:
    Jan 12, 2014
    Posts:
    128
    And another year later, I've just bumped into this issue. For some reason, I'd thought GetComponentInParent always included inactive parents. Anyway, here's my solution:

    Code (CSharp):
    1.  
    2.         static public T GetComponentInParent<T>(this Component component, bool includeInactive) where T : Component
    3.         {
    4.             var here = component.transform;
    5.             T result = null;
    6.             while (here && !result)
    7.             {
    8.                 if (includeInactive || here.gameObject.activeSelf)
    9.                 {
    10.                     result = here.GetComponent<T>();
    11.                 }
    12.                 here = here.parent;
    13.             }
    14.             return result;
    15.         }
    16.  
    No garbage allocations. Fast and simple.
     
  9. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    392
    Doing a bunch of back-and-forth communication between native and managed code was slow at the time of starting this thread, and likely still is.
    Note, "slow" is relative to how fast it would occur if it was done entirely within managed code. Fortunately, it appears Unity corrected these issues in later versions and even let you provide a List<T> to store the results, resulting in no unnecessary allocation, for when you DO need multiple results. If you like, you can compare the speed of your implementation to the one they provide, and you'll see how badly this kind of functionality was needed, especially for large games.
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    .. with lots of Zergling103s running everywhere wrecking my Terran infrastructure... grr.
     
    Zergling103 likes this.
  11. azmi_unity

    azmi_unity

    Joined:
    Dec 13, 2020
    Posts:
    62
    It looks like Unity 2020 onwards exposes IncludeInactive for the singular GetComponentInChildren and GetComponentInParent methods. But only if you access via gameObject.GetComponentInParent.

    Component.GetComponentInParent still does not expose the IncludeInactive parameter.
     
    darkLink_32 likes this.
  12. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    392
    Sounds like they could use codegen to exhaustively spit out every possible variation of GetComponentXXX() and be done with it. :p