Search Unity

transform.Find doesn't work?

Discussion in 'Scripting' started by Dougxing, Aug 25, 2008.

  1. Dougxing

    Dougxing

    Joined:
    Mar 13, 2008
    Posts:
    90
    As far as I can tell, transform.Find("name") is supposed to look through the hierarchy of the object the script is attached to and return the transform of the first object named "name" that it finds. But it just doesn't. I get null back.
    Using gameObject.Find looks through the whole scene, but I need to wire together the hierarchy under a particular object. What am I missing?
     
  2. Dougxing

    Dougxing

    Joined:
    Mar 13, 2008
    Posts:
    90
    Aha... I thought the Find would be recursive the same way GetComponentInChildren is, but it only searches the one level of children...
    Do I have to roll my own recursive search, or is there a better way? I want the transform of a gameobject with a particular name.
     
  3. HiggyB

    HiggyB

    Unity Product Evangelist

    Joined:
    Dec 8, 2006
    Posts:
    6,183
    If your objects will all have unique names then it seems GameObject.Find would world well enough for you. Otherwise if you're specifically after a child object of that name then you'll have to roll your own recursive transform.Find routine.
     
  4. Dougxing

    Dougxing

    Joined:
    Mar 13, 2008
    Posts:
    90
    In my case I did have to do the search myself (fortunately really simple.) I was just thrown by the different behaviors of the Find- and Get-type functions. GameObject.Find will search the whole scene (recursively), I kind of expected gameObject.Find to search just the object hierarchy the script is attached to. (Any reason it shouldn't?) And of course the GetComponentInChildren procedures go deeper than one level.
    Personally, I think it could be more consistent, but I guess it's too late to change the API :).
     
  5. Nodrap

    Nodrap

    Joined:
    Nov 4, 2011
    Posts:
    83
    This is a shame. Surely there is a demand for such a function. I need to find a bone on a verbosely named and long skeleton and would much rather do transform.FindChildRecursively("Head") than give a 5 deep path!
     
    leni8ec and ventsie like this.
  6. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    I use the following myself, but haven't tested it thoroughly:

    Code (csharp):
    1.  
    2. using System.Linq;
    3. using UnityEngine;
    4.  
    5. public static class Helper
    6. {
    7.     public static GameObject FindInChildren(this GameObject go, string name)
    8.     {
    9.         return (from x in go.GetComponentsInChildren<Transform>()
    10.                 where x.gameObject.name == name
    11.                 select x.gameObject).First();
    12.     }
    13. }
    14.  
    Used like so:

    Code (csharp):
    1.  
    2. ...
    3. var cheese = gameObject.FindInChildren("Cheese");
    4. ...
    5.  
     
    Boodums, adhochero and msf-eng92 like this.
  7. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    That works so well, npsf3000. How do you think to use all these weird operators, like "from / where / select"? I've never seen them used before.
     
  8. KyleOlsen

    KyleOlsen

    Joined:
    Apr 3, 2012
    Posts:
    237
  9. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    As Kyle said Linq. It's one of the many awesome features of C#/.NET that are there for the taking :) Another one is helper methods, also demonstrated in that example!

    The great thing about Linq is that even if you've bever seen those keywords before - it should be very easy to follow the logic and write the following instead:

    Code (csharp):
    1.  
    2. using System.Linq;
    3. using UnityEngine;
    4.  
    5. public static class Helper
    6. {
    7.     public static GameObject FindInChildren(this GameObject go, string name)
    8.     {
    9.         foreach (x in go.GetComponentsInChildren<Transform>())
    10.                 if  (x.gameObject.name == name)
    11.                     return x.gameObject;
    12.         throw new System.Exception
    13.                         ("Technically the old version throws an exception if
    14.                        none are found, so I'll do the same here!");
    15.     }
    16. }
    17.  
    Bonus points - see how many of these you are familiar with.
     
    Last edited: Jan 9, 2013
  10. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    Although in this case there's really nothing gained (in readability, performance, etc) over a foreach :p
     
  11. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    Fascinating. Thanks guys.
     
  12. Xypherorion

    Xypherorion

    Joined:
    Nov 23, 2012
    Posts:
    4
    Thank you so much for sharing that. The Find function was driving me absolutely mad...

    Kevin
     
  13. Brenden-Frank

    Brenden-Frank

    Joined:
    Aug 5, 2012
    Posts:
    110
    Here's an option if for whatever reason you can't or don't want to use linq

    Code (CSharp):
    1.     public static GameObject FindInChildren(GameObject gameObject, string name)
    2.     {
    3.         foreach(Transform t in gameObject.GetComponentsInChildren<Transform>())
    4.         {
    5.             if(t.name == name)
    6.                 return t.gameObject;
    7.         }
    8.  
    9.         return null;
    10.     }
     
    Boodums likes this.
  14. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,627
    Without the garbage overhead of foreach and GameObject.GetComponentsInChildren:
    Code (csharp):
    1. public static Transform FindInChildren(Transform transform, string name) {
    2.   if(transform == null) return null;
    3.   int count = transform.childCount;
    4.   for(int i = 0; i < count; i++) {
    5.     Transform child = transform.GetChild(i);
    6.     if(child.name == name) return child;
    7.     Transform subChild = FindInChildren(child, name);
    8.     if(subChild != null) return subChild;
    9.   }
    10.   return null;
    11. }
    12.  
    13. public static GameObject FindInChildren(GameObject gameObject, string name) {
    14.   if(gameObject == null) return null;
    15.   Transform transform = gameObject.transform;
    16.   Transform child = FindInChildren(transform, name);
    17.   return child != null ? child.gameObject : null;
    18. }
    As extension methods:
    Code (csharp):
    1. public static class ExtensionMethods {
    2.  
    3.   public static Transform FindInChildren(this Transform self, string name) {
    4.     int count = self.childCount;
    5.     for(int i = 0; i < count; i++) {
    6.       Transform child = self.GetChild(i);
    7.       if(child.name == name) return child;
    8.       Transform subChild = child.FindInChildren(name);
    9.       if(subChild != null) return subChild;
    10.     }
    11.     return null;
    12.   }
    13.  
    14.   public static GameObject FindInChildren(this GameObject self, string name) {
    15.     Transform transform = self.transform;
    16.     Transform child = transform.FindInChildren(name);
    17.     return child != null ? child.gameObject : null;
    18.   }
    19. }
    20.  
    21. // Usage:
    22. // transform.FindInChildren("Name");
    23. // gameObject.FindInChildren("Name");
     
    Last edited: Apr 1, 2015
  15. Gazzi

    Gazzi

    Joined:
    Aug 13, 2020
    Posts:
    1
    All these methods are not very good because Object.name cause managed string allocations and also has some P/Invoke overhead. I think Unity developers should create a built-in solution.