Category Archives

Unity: Adding children to a GameObject in code and retrieving them

HoloLens, Technical stuff, Unity, Work
2 Comments

Update: The following feedback was pointed to me:

  • You should be careful when using GameObject.Find(…). First, it is bad for performance (note that in my code, I was using it in the Start method and then caching it. This is OK according to documentation, from a performance standpoint). Then, it relies on strings to pass the name of the object you are looking for. That’s not easily maintainable. Instead, it is better to set a public field in your script, and then assign the GameObject that you need in the Unity editor. To keep things simple, I won’t be doing this here but check the blog post here for more information
  • When you remove objects from a scene, you should always start by the last object and then go upwards.

End of update

When you work in the Unity editor, it is quite natural to use hierarchies of objects. For instance, you will have a table object and on this table object you want to place some cups objects, but if you move the table, you want the cups to move too. That’s quite a natural thing to do because it corresponds to the way that things are organized in “real life”. In fact, it even makes sense to have a hierarchy where the parent is an empty GameObject (which will be invisible), this way you can create logical groups of items.

For example, you can go in the Editor’s Hierarchy panel, create an empty GameObject (Right click on the panel and select Create Empty), and name it “Container”. Then you set a transform on the container, for example position = 0, 0, 2 meaning that the Container will be positioned 2 meters in front of the origin point.

2016-06-22_16-35-34

 2016-06-22_16-36-14

Then you can right click on the Container and select 3D Object / Cube to add a cube to this parent. If you so, the new Cube will appear at the exact same position at the Container, as you would expect.

2016-06-22_16-36-57

 2016-06-22_16-39-29

In code however, this is a little more tricky. This is where we realize that there is no true hierarchy of object in Unity but there is a hierarchy of transforms. Let see how to do that:

Creating new objects below the Container

Since we have an empty GameObject named Container in the scene, we can retrieve it in a script with the following code:

_container = GameObject.Find("Container");

The _container field, as you would expect, is of type GameObject. If you inspect it in the debugger, you will see that it is placed, as expected, at (0, 0, 2) (this is the transform.localPosition).

Now let’s create a new Cube and add it to the GameObject. Unfortunately, we notice that there is no Add or AddChild or similar method on the GameObject class. This is where we need to work with Transforms. The following code helps:

var newCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
newCube.transform.SetParent(_container.transform, true);
newCube.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);

In this snippet, we create a new Cube of 20 cm size, and we specify that the Cube’s transform’s parent is the Container’s transform (incidentally, Unity says that you should use SetParent and not the parent property directly, even though it can be set. Welcome to the ugly world of scripting ;). This will effectively create a hierarchy like we have in the editor. But there is a catch! If you run the code now, you will notice that the new Cube doesn’t appear, like you would expect, at (0, 0, 2) but it appears at (0, 0, 0). If you run this in a HoloLens, you will get pretty confused because (0, 0, 0) is probably going to be your head, and so the Cube is placed around your head and you won’t even see it until you move to a different location).

If you inspect the newCube in the debugger, you will see that its localPosition is set to (0, 0, –2). So it seems that Unity went out of this way to misunderstand what I was trying to do, and forced the newCube location to be at (0, 0, 0) globally, which means (0, 0, –2) relative to the parent. Ugh…

When reviewing the documentation, I found an overload of SetParent which takes a parameter named worldPositionStays of type bool. I thought that was promising, but setting this parameter to true didn’t change a thing. I also tried variations of the calls above. The newCube still appeared at (0, 0, 0). So to fix it, I forced the localPosition to be at (0, 0, 0) (relative to the Container). This way the global position of the Cube is (0, 0, 2).

newCube.transform.localPosition = new Vector3(0f, 0f, 0f);

This sounds unnecessarily complicated, so if I am doing things the wrong way, please add your knowledge in the comments, thanks!

Retrieving the children

Now how can we retrieve the children and iterate on them? For instance, this can be useful if you want to clean the scene by removing all the Cubes, but leaving the Container in place so the user can add more Cubes. Here too, we need to work with the transform hierarchy. Here is the code:

Note: For this simple demo I am not going to update the code BUT as was pointed in the comments, you should rather remove objects from the end of the hierarchy!

foreach (Transform t in _container.transform)
{
    var cube = t.gameObject;
    Destroy(cube);
}

In this code, we get the transform property of the Container and then we iterate through all its children. Then we retrieve the gameObject property of each transform, which corresponds to the Cube that we want to delete, which is done by calling the Destroy method.

Hopefully this quick tutorial will help you when you do this kind of things. Structure is very nice, but structuring a scene in code is a bit tricky. This should save you some time. Now again, if you see something that can be improved, don’t hesitate to point it out in the comments.

Happy coding!
Laurent

GalaSoft Laurent Bugnion
Laurent Bugnion (GalaSoft)

Share on Facebook