Search Unity

GetComponentsInChildren - not parent and children

Discussion in 'Scripting' started by topofsteel, Jan 11, 2014.

Thread Status:
Not open for further replies.
  1. topofsteel

    topofsteel

    Joined:
    Dec 2, 2011
    Posts:
    999
    Is there a way to GetComponentsInChildren without getting the parent also? I use the following code to create an array of Transtorms from an empty gameobject with a number of gameobjects inside. It always gets the parent transform also. I can filter it out by name, but is there a better way? Thanks.

    Code (csharp):
    1.     Transform[] path;
    2.  
    3.     void Awake()
    4.     {
    5.         GameObject points = GameObject.Find("Path_01");
    6.         path = points.GetComponentsInChildren<Transform>();
    7.     }
    8.  
    9.     void Start()
    10.     {
    11.         Debug.Log(path.Length.ToString());
    12.     }
     
  2. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    973
    There is a way to do this without getting the parent.

    But this is not using GetComponentsInChildren

    Code (csharp):
    1. foreach (Transform tr in transform)Debug.Log(tr.name);
     
    topofsteel and TheDevloper like this.
  3. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    This will only get you the top level of children.
    If you go this approach, you would have to repeat it recursively on each child.

    Code (csharp):
    1.  
    2. void PrintAllChildren (Transform T){
    3.   foreach (Transform child in T){
    4.     print(child.name);
    5.     PrintAllChildren(child);
    6.   }
    7. }
    8.  
    or if you really need a collection of all child transforms, just use a List<T> and add instead of print.
     
  4. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,658
    Code (csharp):
    1.  
    2. var transforms = new HashSet<Transform>(GetComponentsInChildren<Transform>());
    3. transforms.Remove(transform);
    4. path = transforms.ToArray();
    5.  
     
    I_X_I, hms0589, McGravity and 5 others like this.
  5. CrandellWS

    CrandellWS

    Joined:
    Oct 31, 2015
    Posts:
    178

    Just do not use
    Code (csharp):
    1. path[0]
    start anything with
    Code (csharp):
    1. path[1]


    Read Below: http://forum.unity3d.com/threads/ge...-not-parent-and-children.222009/#post-2380166
     
    Last edited: Nov 13, 2015
    radiantboy likes this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    The order of components returned by GetComponentsInChildren is not guaranteed. I would not suggest relying on this approach.
     
  7. CrandellWS

    CrandellWS

    Joined:
    Oct 31, 2015
    Posts:
    178
    That is true, but from what I have found the parent has always been in 0, but again you are correct not guaranteed...
     
    VishrutiFaldu likes this.
  8. CrandellWS

    CrandellWS

    Joined:
    Oct 31, 2015
    Posts:
    178
    BoredMormon you like the following comment
    but it seems I am unable to use ToArray();
    Code (csharp):
    1.  path = transforms.ToArray();
    So I adjusted to
    Code (csharp):
    1.  
    2. List<Transform> transforms = new List<Transform>(GetComponentsInChildren<Transform>());
    3. transforms.Remove(transform);
    4. path = transforms.ToArray();
    5.  
    Code (csharp):
    1.  
    2. I guess Code (csharp): is default?? May not be what is quoted? I have only been using C# for about a week.
    3.  
     
  9. Binbag42

    Binbag42

    Joined:
    Apr 14, 2014
    Posts:
    6
    Alternatively you can use transform.GetChild() to cycle through the children while keeping their order. That would give something like (just change T by the type of component you are interested ie Transform in your example):

    Code (CSharp):
    1.         T[] path = new T[parent.transform.childCount];
    2.         for (int i = 0; i < parent.transform.childCount; i++)
    3.         {
    4.             path[i] = parent.transform.GetChild(i).GetComponent<T>();
    5.         }
    This works if every child has the component you are interested in, Transform is a good example.
     
    Last edited: Feb 11, 2017
    khaled24 and MaratG2 like this.
  10. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    Just do for i = 1 not for i = 0 this skips the parent. Make the array this way. Then do i-1 on your array.

    I'm on phone can't type out entire example
     
  11. Binbag42

    Binbag42

    Joined:
    Apr 14, 2014
    Posts:
    6
    It may have changed over time but as far as I can tell from Unity documentation and my own experimentation, transform.GetChild(0) does return the first child in the hierarchy and not the parent gameobject.
     
  12. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    I think transform.GetChild is a new function that didn't exist before Unity 5.5
     
  13. AndyGainey

    AndyGainey

    Joined:
    Dec 2, 2015
    Posts:
    216
    Documentation suggests it has existed since at least 4.2. I certainly remember using it throughout all 5.x versions.
     
    Kiwasi likes this.
  14. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Ok, maybe I just didn't see it before for some reason.
     
  15. topofsteel

    topofsteel

    Joined:
    Dec 2, 2011
    Posts:
    999
    Interesting flurry of activity after all of these years. And I needed to use something like this again. Here is what I ended up with.

    Code (CSharp):
    1.     public Transform locationContainer;
    2.     private Transform[] locations;
    3.  
    4.     void Awake()
    5.     {
    6.         int i = locationContainer.GetComponentInChildren<Transform>().childCount;
    7.         locations = locationContainer.GetComponentsInChildren<Transform>();
    8.  
    9.         locations = new Transform[i];
    10.         for (int j = 0; j < i; j++)
    11.         {
    12.             locations[j] = locationContainer.GetComponentInChildren<Transform>().GetChild(j);
    13.         }
    14.     }
     
    Last edited: Feb 17, 2017
  16. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    I just wanted to chime in and say that this is such a horribly named method, and I am surprised it has not been updated after all these years. GetComponentInChildren should do exactly that! This nonsense where it returns components on the calling Component itself is just silly.

    Of course, it's probably too late to change now, as a lot of projects may rely on the current behaviour, however they could implement a non breaking change by simply adding an optional Boolean parameter called "includeComponentsOnSelf" or something similar, with it's default value set to true. Then every body wins.

    While they're at it, add another optional integer parameter that specifies how deep the search should go, with it's default value set to 0. A value of 0 would indicate the search should include all children/grandchildren/etc. (the current behaviour), while a different value would limit the search to that many levels deep (i.e., a value of 1 would only include components on immediate children).
     
  17. Vadimskyi

    Vadimskyi

    Joined:
    Jan 13, 2020
    Posts:
    12
    Bumping this old thread.
    Was there a change in Unity API, maybe some new method added?
    Or does we still need to use this hack:

     
  18. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    Wouldn't the most simple solution just be an if check or LINQ? Doesn't seem like a hack to me, just a weird API design. In any case an if check shouldn't slow you down at all.

    Code (CSharp):
    1. var transforms = GetComponentsInChildren<Transform>();
    2. foreach(var transform in transforms)
    3. {
    4.    //ignore mine
    5.     if(transform == this.transform)
    6.         continue;
    7. }
    8.  
    9. OR
    10. var transforms = GetComponentsInChildren<Transform>().Where(x => x != this.transform);
    11. foreach(var transform in transforms)
    12. {
    13.     //do something
    14. }
    EDIT: As for the original question, even though it's years old, this is how I'd approach it
    Code (CSharp):
    1.  
    2. List<Transform> pathPoints;
    3.  
    4. void Start()
    5. {
    6.     pathPoints.AddRange(GetComponentsInChildren<Transform>().Where(x => x != this.transform));
    7. }
     
  19. Vadimskyi

    Vadimskyi

    Joined:
    Jan 13, 2020
    Posts:
    12
    It is a solution, but it is not resolves a problem of misleading developers in to thinking that method named "GetComponentsInChildren" should behave exactly as it named.
    Even as simple as <summary>The return result also includes parent object components.</summary> would help a lot.
     
  20. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    The <summary> for this is only on some overloads for some reason. :/
     
  21. topofsteel

    topofsteel

    Joined:
    Dec 2, 2011
    Posts:
    999
    I found a much better way of doing this. I've created empty classes for the container and the locations in the scene. I'm automatically finding the container and easily collecting only the locations.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ObjectsFromLocation : MonoBehaviour
    4. {
    5.     public GameObject somePrefab;
    6.  
    7.     private LocationContainer locationContainer;
    8.     private Location[] locations;
    9.  
    10.     void Awake()
    11.     {
    12.         locationContainer = FindObjectOfType<LocationContainer>();
    13.         locations = locationContainer.GetComponentsInChildren<Location>();
    14.  
    15.         foreach(Location l in locations)
    16.         {
    17.             Instantiate(somePrefab, l.transform.position, Quaternion.identity);
    18.         }
    19.     }
    20. }
     
  22. RubenHeeren

    RubenHeeren

    Joined:
    Dec 9, 2019
    Posts:
    9
    Ye so I solved it like this (mesh was in this case the parent GameObject I didn't want in my enumerable):

    Code (CSharp):
    1.  
    2. List<GameObject> meshes = new List<GameObject>();
    3.  
    4. for (int i = 0; i < thisUnit.mesh.transform.childCount; i++)
    5.             {
    6.                 foreach (var mesh in thisUnit.mesh.transform.GetChild(i).GetComponentsInChildren<Transform>())
    7.                 {
    8.                     meshes.Add(mesh.gameObject);
    9.                 }
    10.             }
     
    Last edited: Feb 22, 2021
  23. SniperED007

    SniperED007

    Joined:
    Sep 29, 2013
    Posts:
    345
    Would be nice if GetComponentsInChildren actually just got the componentes in the children like it says.
     
    Circool and SiMULOiD like this.
  24. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,925
    It does exactly what the documentation says: "Returns all components of Type type in the GameObject or any of its children. Works recursively."
     
  25. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,647
    I think the problem is the naming suggests that it would just receive Components from its children, but includes itself.
     
  26. SniperED007

    SniperED007

    Joined:
    Sep 29, 2013
    Posts:
    345
    they should add an override to ONLY get childrens components.
     
Thread Status:
Not open for further replies.