Get all transforms in all children in a specific GameObject

So, what I need is to store every rotation of every children of a GameObject, in order to restore their original rotation as soon as I need it.
So what I’m doing here is I call a function which stores the rotations in a List of Vector3 (I’m using eulerAngles).

I did the following:

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;

    public class Example : MonoBehaviour {
        List<Vector3> rotations = new List<Vector3> ();

        void SetPositions () {
            foreach (Transform t in GetComponentsInChildren <Transform> ())
                rotations.Add (t.eulerAngles);
        }

Then, I call that function inside Start ()

        void Start () {
            SetPositions ();
        }

Then, every time I need to restore rotations, I call this:

        void restore () {
            for (int a = 0; a < GetComponentsInChildren <Transform> ().Length;)
                GetComponentsInChildren <Transform>() [a] = rotations [a++];
        }
    }

The real issue being, each time I call that restore () function some child objects are not rotated correctly. Not only that, but an error shows up in the console stating that the index is out of range.
So I just change this piece:

    GetComponentsInChildren <Transform>() [a] = rotations [a++];

To this:

    GetComponentsInChildren <Transform>() [a]
    = rotations [a++];

And the error highlites the “rotations [a++]” line.
Changing the for cycle to for (int a = 0; a < GetComponentsInChildren ().Length; a++)
and rotations [a++] to rotations [a] doesn’t seem to do anything.
I tried to Debug.Log (t.name+": "+t.eulerAngles); and each children name, along with the angles, shows up perfectly in the console.

I even tried changing List to List , but it didn’t work.
Also, when I tried that in previous projects, it turned out every element of that list updated each time the real Transform did, and I belive it is because the Transform class can’t be Instantiated.

So what am I doing wrong?

Far easier to modify the transforms of all children by using:

foreach(Transform child in transform) { }
1 Like

Mh, I tried that, but it only gives me the “first layer” of children, I actually need to get every one of them.

Good point. In that case you should probably assign the array to a temporary variable to make it easier to use and make adjustments to.

Transform[] childList = GetComponentsInChildren<Transform>();

It’ll make it far easier to figure out where exactly this is screwing up.

The number of children (and thus the number of transforms) isn’t necessarily going to always be the same right? The moment a new child is added, the position cache is suddenly shorter than the total number of current transforms, and vice-versa if a child is removed. I think in order to pull this off you need a more definitive way to compare the memory to the current object, for instance by using a dict that holds the object reference and the old Vector3 value. That way you can just keep bringing up the current children list and say “if it HAS a position cache now, then return it to that state- if it doesn’t, then make one” or something. Get what I’m saying?

The issue you have here is that rotations are inherited though the hierarchy. If you rotate a parent, it’s children will rotate.

Also, Lysander’s tip is good, it’s much faster and you can easily make it recursive like so:

static List<Transform> GetAllTransforms(Transform parent)
{
        var transformList = new List<Transform>();
        BuildTransformList(transformList, parent);
        return transformList;
}

private static void BuildTransformList(ICollection<Transform> transforms, Transform parent)
{
        if (parent == null) { return; }
        foreach (Transform t in parent)
        {
            transforms.Add(t);
            BuildTransformList(transforms, t);
        }
}

If you use transform.localEulerAngles then the rotations should be correct. I’m not sure I’m a fan of storing rotations this way though. I’m curious, what’s the problem you’re trying to solve here?

1 Like

Well, the number of children is not going to change, so I don’t need to consider the event of a children being removed or added. Or at least, I don’t think they should. I will try and log the number of children over time to make sure they won’t actually change.

I have an object which contains a number of children objects.
What I’m trying to accomplish is let the user decide the orientation of these objects and save them (using serialization), then, when needed, restore every rotation to its original value.
I actually did manage to save the rotations to a file and restore them to that rotation, every children gets rotated correctly, but I also need to restore them to the original state:
what I did initially was treat the original state just like any other file and simply save it immediatly as soon as the application loads.
But it didn’t work since as soon as i tried restoring that file some children were not rotated correctly for some reason.
I also noticed that if I let the application load and I manually save a state where no children have been rotated, it does get restored succesfully and does not return any error.
So I decided not to save any file for the default state, instead I would have saved every rotation into a List and I would have restored them to the values of that list.
Unfortunatly this does not work as well, since some objects are again not rotated correctly.
I’ll try these two functions you gave me and I’ll see if these work (I don’t really know how recursive functions work that much yet).

EDIT: I should also point out that the gameobject here has a lot of children (more than 300)

Well, this time apprently the List turned out empty:

List rotations = new List ();
rotations = GetAllTransforms (model.transform);
Debug.log (rotations.Count); //0

You should probably use a Dictionary, not a List, as the order of returned results from GetComponentsXYZ isn’t guaranteed. You might in the future have a very hard to debug error where you’re assigning the wrong transform to the wrong child because of your assumption that each list is returned in the same order. Hell, it might even fix the issue you’re having now.

1 Like

Thanks for the advice, I didn’t know this, but how should I implement it?
As far as I know, a Dictionary lets you decide a key for every entry of the array, but since the results’ order of GetComponents is not always the same, how can I detect if rotations [key1] is referring to the same object as GetComponentsInChildren () [0] ?

Use the transform itself for the key.

Dictionary<Transform, Vector3> rotations = new Dictionary<Transform, Transform>();

void SetPositions()
{
    foreach(Transform t in GetComponentsInChildren<Transform>())
        rotations.Add(t, t.eulerAngles);
}

void RestorePositions()
{
    foreach(Transform t in GetComponentsInChildren<Transform>())
    {
        if(rotations.ContainsKey(t) == true)
        {
            t.eulerAngles = rotations[t];
        }
        else
        {
            Debug.Log("Transform not found in dictionary, GameObject: " + t.gameObject.name);
        }
     }
}
1 Like

There’s absolutely nothing saying that the object reference can’t be the key value itself. You would access the dictionary using the reference, for example Dict[childTransform] which would then return the cache of the Vector3 you stored there. Dicts are not just two-dimensional Lists, which is why the “key” has to be unique within the Dict.

1 Like

Lost by 5 seconds…

1 Like

Oh, so apparently some child object do return the “Transform not found in dictionary”, but aside from that every child object now does rotate correctly apparently.
Thanks for the help guys, I’ll check what’s happening to the objects giving me the error.