Search Unity

Finding Child Object

Discussion in 'Scripting' started by chrisk, Jun 9, 2009.

  1. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    I'm little frustrated finding a child object of an object.

    I have a "muzzle" (muzzle mesh object) child object on a Gun object.

    but print(gun.transform.Find("muzzle").gameObject) gives NullReferenceException

    "Hierarchy" clearly shows "muzzle" object on the Gun yet I can't get hold of "muzzle" child object.
    What could be wrong?

    And I must say finding child game object through transform is really ugly.
    And child object depending on transform is simply wrong. You can have logical child without transform.

    Thanks.
     
  2. Tempest

    Tempest

    Joined:
    Dec 10, 2008
    Posts:
    1,286
    Can you give a little more detail about the scene?

    gun.transform makes it sound like you have a third object. Are you sure "gun" is properly assigned to this third object?

    Or is the script attached to gun, and trying to find it's child, in which case you would just need transform.Find(). I just tested your print statement like this, and it worked fine.
     
  3. duck

    duck

    Unity Technologies

    Joined:
    Oct 21, 2008
    Posts:
    358
    If you're doing this within a script, it's often better to use public variables, which you can then drag objects into in Unity's IDE.

    For example, in your 'gun' script, you could have the following:

    Javascript:
    Code (csharp):
    1. var muzzle : GameObject;
    or C#:
    Code (csharp):
    1. public GameObject muzzle;
    This variable should then become visible when you select your gun in the hierachy (assuming the gun script is attached). You can then drag your muzzle GameObject on to that variable.

    Then in your script, you don't need to 'Find' anything. Just use your 'muzzle' variable, which will be a reference to that gameObject.

    hope this helps,

    - Ben
     
  4. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Yes, I have a script that needs access to the child object. So in my script, I'm just doing transform.Find("muzzle"). I said gun.transform.Find("muzzle") to illustrate that "muzzle" is a child object of a gun.

    Anyway, I just need to make "muzzle" to flash on/off when firing and I need the "muzzle" to attached to the front of the gun.
    "muzzle" is exported with the gun so that when gun is animated, "muzzle" move with it. This answers that I cannot use "muzzle" as separate variable to hold the object. If I do, it won't move with the gun's animation. I can have the muzzle move with the gun if I attach it as child of the muzzle bone but then I'm facing the same problem. I can't get hold of child object.
     
  5. Tempest

    Tempest

    Joined:
    Dec 10, 2008
    Posts:
    1,286
    Is muzzle inactive? To hide it? If so it won't respond to a Find() attempt.

    If you do the suggestion made in the previous post, but making a public variable which is assigned to the child, then you will won't have a problem "finding" it. Since it's then assigned, I believe it's easy to just turn it off/on then.
     
  6. fivearchers

    fivearchers

    Joined:
    Apr 17, 2009
    Posts:
    716
    I eventually found I need to include a path, so to find my "gm1" (my muzzle actually heh), I needed to find it like so:

    "mesh AAA Turret/meshROTATE/meshELEVATE/gm1"

    The "mesh AAA Turret" is essentially the root mesh in the scene. It would actually be in my ENEMYTURRET prefab, which I don't need to reference (I guess because that's the level you start at)

    What's kind of a pain is that I need an extra variable containing the "mesh AAA Turret/", as I have guns with "mesh Missile Turret/" and so forth, but at least below the inital object everything is named the same for all my turret models.

    This works, but doesn't seem quite right to me, like I wish I could just grab the gm1 transform from the get-go!
     
  7. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    @Tempest, "muzzle" is not inactive when I first trying to access.
    Like I said, I need the muzzle flash to move with the gun. Gun is animating when fired. To make muzzle flash to animate with the gun, it needs be attached to the muzzle bone, front of the gun. This means that I need to find muzzle bone and I have the same problem.
     
  8. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    @quentinp, do you mean that I need to use full path? "muzzle" is burried deep in the hierarchy. It sound discouraging.
     
  9. Tempest

    Tempest

    Joined:
    Dec 10, 2008
    Posts:
    1,286
    So does this mean that you're creating, and then deleting muzzle, or simply showing, then hiding muzzle?


    If it's "buried deep" then it's not a child of the parent, but a child, of a child, of a child..etc of a parent.

    If you're not creating/deleting the muzzle, then a public variable will allow you to control it, regardless of where it is in the hierarchy.
     
  10. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    @Tempest, yes "muzzle" is child of child of child... of gun. Is that the problem? Do I need to give full path name to find it?

    If you use the variable to hold "muzzle" object, how to then attach it to front of the gun. Like I said, the gun is animating when firing. Gun is skinned mesh and I believe when skinned mesh is animating, it will animate independently unless "muzzle" is attached to the proper bone.
     
  11. Tempest

    Tempest

    Joined:
    Dec 10, 2008
    Posts:
    1,286
    Yes, the problem is that muzzle is not, in fact, a direct child of gun, and thus is not found without respecting the hierarchy which is established.

    You need to either put in the full hierarchy path, or create a public variable which accomplishes the exact same thing you're doing when you do the transform.Find().

    Creating a public variable in the script, and attaching to the muzzle to it, does not change the hierarchy, it simply allows your script to know what GameObject (muzzle in this case), to talk to.


    Code (csharp):
    1. public GameObject muzzle;
    can accomplish the same as

    Code (csharp):
    1. GameObject muzzle = transform.Find("/../../muzzle").gameObject;


    Edit: In the first case, a field would appear in the inspector for you to drag the muzzle object to. This would link your script, to that object. What you're doing with the transform.Find is the exact same function, which is linking the game object to your script.
     
  12. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    I now understand about the first method.
    I didn't know that I can drag any child object to a variable.
    Thanks for the tip.

    And using full path seems like a big mess. In the case of gun, it's not so bad. It's about 4 level deep, however, if I were to find a finger bone of a character, it could be ~10 level deep.
    I can't believe that there isn't an API that traverse children recursively. Bummer.
     
  13. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    transform child relationships indicate a spatial relationship.

    logical relationships are often implemented using public variables pointing to components you need for scripting. These public variables do not have any spatial effect. In other words: make a public variable "muzzle", go to your script the game hierarchy window, drag the muzzle into the public variable and you will be able to access muzzle from your script, while it will still depend on the hierarchy and bones for its position.
     
  14. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Yup, I now understand the easy way.
    Thanks.
    It works in this case, however, there might be a case I need to find child object dynamically.
    Find(), requiring full path seems like a huge limitation for deep heirarchy. There should be an API that find child object recursively, don't you think?
     
  15. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    I agree. That's why I wrote one a long, long time ago :p.
     
  16. Tempest

    Tempest

    Joined:
    Dec 10, 2008
    Posts:
    1,286
    Thanks for explaining that tomvds. That's what I was trying to say, but I just wasn't explaining it well.

    @Chrisk
    Well, you could have a script which, when searching, looks deeper than the first level of children. You can also assign tags, and do searches by tags, which remove the need for remembering/discovering hierarchy.
     
  17. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    I'll keep that in mind too. But doesn't the tag search from the global namespace instead from the current object?
    This probably means the tag needs to have unique name as well to avoid name collsion, correct?

    My observation of Unity API is that it's very powerful and flexible yet, it's missing some basic APIs, especially dealing with finding GameObjects, Components and its children; Some requires "types" instead of "name" string; Some only work one level deep, some works recursively; And convoluted to access child object, e.g. this.transform.Find("root/child/child/child/target").gameObject instead of this.FindChild("target")

    Anyway, thanks for everyone who helped me.
     
  18. TheAlchemist42

    TheAlchemist42

    Joined:
    Apr 17, 2008
    Posts:
    68
    You can also use GameObject.BroadcastMessage to send a message down the hierarchy to all children, and have the child react to it if needed.
     
  19. fivearchers

    fivearchers

    Joined:
    Apr 17, 2009
    Posts:
    716
    Well i'm glad to see i'm not doing it wrong :)
     
  20. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    Code (csharp):
    1. public GameObject GetChild( string name )
    2. {
    3.    Component[] transforms = GetComponentsInChildren( typeof( Transform ), true );
    4.  
    5.    foreach( Transform transform in transforms )
    6.    {
    7.       if( transform.gameObject.name == name )
    8.       {
    9.          return transform.gameObject;
    10.       }
    11.    }
    12.  
    13.    return null;
    14. }
     
  21. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    Oops. Misread it. Never mind :p
     
  22. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    @AngryAnt, cool. yeah things like this would be useful to everyone. It's not hard to write but I'm wondering when I write my own, it may not be the most efficient way and why user have to write such a basic API themselves. Why can UT dev add it into the standard API, of course with some caveat about abuse.

    We need complete set of Get or Find to Component or GameObject using ID, Type, Name, Tag from its children or from the Global list
    Right now, API is completely unorthogonal. If missing, users have to fill the gap.

    Thanks.
     
  23. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    This is not official UT policy (since I'm not aware exactly what it is), but personally I prefer APIs to be slim and only solve the problems which I commonly face and those which are larger in complexity. Obviously they need to be well designed as well - enabling me to add solutions for corner cases like this easily.

    You're quite right that any search method (such as this) is not very effective which I guess is exactly why it is not part of the API: Its ineffective and not the preferable way to solve the problem.

    The preferable way of referring to a child GO somewhere down the chain would be to drag-drop assign it to a public variable - like described earlier in this thread.
     
  24. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Efficiency is one thing but getting the job done is another. When things are static, I agree that public variable is the right way to go, if dynamic, however, there is no other way other than searching through the hierarchy, whether efficient or not it has to be done. If there is only one usecase, it's still necessary. Why would we have to write when such case arise?? I don't think everyone can completely get away with it because we cannot say for sure we never use it. I rather UT to provide it to us even if it's not used often. I trust them to write it at least as efficient as I can write, not that I too lazy to write it myself.

    ps. Adding few more APIs will not affect performance or efficiency. I think being orthogonal adds many benefits especially when a new user is trying to learn the system. I spend needless time trying to understand why it's designed this way. I rather start using API with low learning curve and as I learn, I can replace with more efficent method.
    At the begining I just want things to work without pulling too much of my hairs.
    Isn't orthogonality being very important espect designing APIs without cutting corners anyway?
     
  25. fivearchers

    fivearchers

    Joined:
    Apr 17, 2009
    Posts:
    716
    One thing that might work (now that I know it exists heh) - the SendMessage function may allow you to have your muzzle do something without directly accessing it?

    Code (csharp):
    1.  
    2. gameObject.SendMessage("DoMuzzle");
    3.  
    And attached to your muzzle:

    Code (csharp):
    1.  
    2. function DoMuzzle(){
    3.   //stuff
    4. }
    5.  
     
  26. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    It will work but I'm not sure if sending message down to all its children everytime you fire a weapon is the right way to do it.
    Besides, having many scripts might make scene setup complicated. Without a visual script editor, it's hard to keep track of how all the scripts are connected together.

    Anyway. I think I'll resort to "public variable" method in this case.
    Thanks.
     
  27. Mackey@ehsiplus.com

    Mackey@ehsiplus.com

    Joined:
    Jun 27, 2009
    Posts:
    45
    Hey Chris (Or Anyone Else)

    Please let me know the details on how to get muzzle flash working with a animated fire clip.
     
  28. DrHotbunz

    DrHotbunz

    Joined:
    Feb 14, 2009
    Posts:
    315
    Angry Ant I understood your position that
    but respectfully I have to disagree about finding children being not complex enough to be covered by the API.

    I think APIs should simplify our life as much as possible, (which Unity does greatly) however finding children is only simple if you understand about transforms properties being able to be leveraged for such a purpose. I think it should be covered API side.
     
  29. DrHotbunz

    DrHotbunz

    Joined:
    Feb 14, 2009
    Posts:
    315
    By the way here is the javascript equivalent of the script above:

    Code (csharp):
    1. function GetChild( name : String) : GameObject {
    2.  
    3.    transforms = GetComponentsInChildren( typeof( Transform ), true );
    4.     var i = 0;
    5.     for (i=0;i<transforms.length;i++)
    6.    {
    7.       if( transforms[i].gameObject.name == name )
    8.       {
    9.          return transforms[i].gameObject;
    10.       }
    11.    }
    12.    return null;
    13. }
     
  30. Jared M

    Jared M

    Joined:
    Apr 22, 2009
    Posts:
    59
    I am running into this same problem here.

    I have a few levels in my game, each level having varying numbers of a type "GameItem". I am storing all of these below one parent game object. It is very easy to store the transforms of these game items in an array, but so far have not figured out how to access the functions for the "GameItem" class that is attached to these objects. The only thing I can access are things off the transform type, so therefore I can't really do anything with these items.

    Doing it with a public variable just isn't a good way to do it IMO, since the numbers vary according to the level, and they all do different things and have different names (yet share certain attributes in the GameItem class). I am using public variables for certain other things where it makes fine sense to do so. However in this case, it seems doing it dynamically is THE way to do it. Is this just not the right way to use Unity? Is there an easy way to accomplish the task of using the custom class functions off a transform object?
     
  31. grimmy

    grimmy

    Joined:
    Feb 2, 2009
    Posts:
    409
    Erm..when I run this Javascript function it just seems to return the parent gameObject.. ie whatever string I pass in it seems to return a gameobject with the same name...itself.

    ?
     
  32. vished

    vished

    Joined:
    Oct 24, 2010
    Posts:
    26
    Come on guys this is archaic! We have known hierarchies (and scripts) being imported from external 3D apps and we can't reference them directly with namespaces without writing our own APIs? How is having two gameObjects with the same name a good idea? What is the "Global List"? This is all completely counter intuitive and boring. If all gameObjects require a transform just make all gameObjects transforms with components (shapes) like Maya. Creating public variables by dragging and dropping in the interface is tedious and prone to human error. The whole point of using scripts is to glue a scene together programmatically.
    http://forum.unity3d.com/threads/63...ound-prefabs-GameObjects-types-and-references

    So... since Unity allows for objects with the same name the only way to distinguish between them is by listing them in arrays
    http://benbritten.com/2009/09/06/unity-tip-for-the-day-always-name-your-new-gameobjects/
     
    Last edited: Feb 16, 2011
  33. TRuoss

    TRuoss

    Joined:
    Dec 5, 2012
    Posts:
    85
    the code here does not work for me, so i modified it.

    Here is what worked for me:

    Code (csharp):
    1. public static GameObject GetChildGameObject(GameObject fromGameObject, string withName)
    2.     {
    3.         //Author: Isaac Dart, June-13.
    4.         //Modified: Timo Ruoss, Aug-13.
    5.         var count = fromGameObject.transform.GetChildCount();
    6.         List<Transform> ts = new List<Transform>();
    7.  
    8.         for (int i = 0; i < count; i++)
    9.         {
    10.             ts.Add(fromGameObject.transform.GetChild(i));
    11.         }
    12.  
    13.         foreach (Transform t in ts)
    14.         {
    15.             if (t.gameObject.name == withName)
    16.                 return t.gameObject;
    17.             else
    18.             {
    19.                 var result = GetChildGameObject(t.gameObject, withName);
    20.                 if (result != null)
    21.                     return result;
    22.             }
    23.         }
    24.  
    25.         return null;
    26.     }
    should not be used every frame.