Iterating through Children: Why isn't this working?

Brief intro, here’s the hierarchy i’m working with. Note the many duplicate “Manipulator…” objects several layers deep.

I’m attempting to programmatically delete them all and then replace them with a single clean one
(yes i know this is easy to do manually. I’m writing a tool for automating things)

The section of relevant code for this is

void SetupManipulators()
    {
        for(int i  = 0; i < ManipulatorParentNames.Length; i++)
        {
            string s = ManipulatorParentNames[i];
            Transform t = ObjectTools.FindChildByName(s, transform);

            foreach (Transform v in t)
            {
                Debug.Log("Checking: " + v.gameObject.name);
                if (v.gameObject.name.ToLower().Contains("manipulator"))
                {
                    Debug.Log("Destroying: " + v.gameObject.name);
                    DestroyImmediate(v.gameObject);
                }
            }

            GameObject g = Instantiate(manipulator);
            g.transform.parent = t;
            g.transform.localPosition = ManipulatorLocalPositions[i];
        }
    }

There are some references to arrays and functions there. ManipulatorParentNames currently only contains one word, “Jaw”

Here’s the debug output from running that code, on that hierarchy (setup complete is in the function that called this one)

And how the hierarchy looks afterwards

What the devil is going on here?
As can be clearly seen, it checks the first and second child, but then skips every alternating one starting from the third.

Explain this madness please ;-;

I’m guessing the “stack is falling”… i.e.
get 1 delete 1 => 2 becomes 1 mwhahahah I’m not going to get deleted, 3 become 2, 4 become 3 etc.
get 2 delete 2 => 1 is still 1, 3 becomes 2 mwhahahah I’m not going to get deleted, 4 becomes 3 etc.

so a while loop removing the first one until the length is empty would work

1 Like

When you call DestroyImmediately from inside your foreach loop, I suspect it’s messing with the loop iterators because you’re immediately removing the child from the hiearchy. To do a safe delete, you’d want to do something like:

List<Transform> toBeDeleted = new List<Transform>();
foreach (Transform v in t)
{
    Debug.Log("Checking: " + v.gameObject.name);
    if (v.gameObject.name.ToLower().Contains("manipulator"))
    {
        toBeDeleted.Add(v);
    }
}
for (int i = 0; i < toBeDeleted.Count; i++)
{
    var v = toBeDeleted[i];
    Debug.Log("Destroying: " + v.gameObject.name);
    DestroyImmediate(v.gameObject);
}
toBeDeleted.Clear();

Hope that helps.
Owen

2 Likes

I tried it myself and maybe just a little change will help. I don’t know exactly how you have implemented it, i’ll give it a try.

if this gives you back the Transfrom “Jaw” in your hierarchy

than i only changed

void SetupManipulators()
    {
        for(int i  = 0; i < ManipulatorParentNames.Length; i++)
        {
            string s = ManipulatorParentNames[i];
            Transform t = ObjectTools.FindChildByName(s, transform);
            Transform[] allTransfomrsInJaw = t.gameObject.GetComponentsInChildren<Transform>();

            foreach (Transform v in allTransfomrsInJaw)
            {
                Debug.Log("Checking: " + v.gameObject.name);
                if (v.gameObject.name.ToLower().Contains("manipulator"))
                {
                    Debug.Log("Destroying: " + v.gameObject.name);
                    DestroyImmediate(v.gameObject);
                }
            }
            GameObject g = Instantiate(manipulator);
            g.transform.parent = t;
            g.transform.localPosition = ManipulatorLocalPositions[i];
        }
    }

Tried it out in an CustomEditorScript. Because i couln’t find the ObjectTools class i made something different.

void SetupManipulators()
    {
        for(int i  = 0; i < ManipulatorParentNames.Length; i++)
        {
            string s = ManipulatorParentNames[i];
            GameObject t = GameObject.Find(s);
            Transform[] allTransfomrsInJaw = t.GetComponentsInChildren<Transform>();

            foreach (Transform v in allTransfomrsInJaw )
            {
                Debug.Log("Checking: " + v.gameObject.name);
                if (v.gameObject.name.ToLower().Contains("manipulator"))
                {
                    Debug.Log("Destroying: " + v.gameObject.name);
                    DestroyImmediate(v.gameObject);
                }
            }
            GameObject g = Instantiate(manipulator);
            g.transform.parent = t;
            g.transform.localPosition = ManipulatorLocalPositions[i];
        }
    }

The other option is to iterate through the loop backwards. That way none of the indexers get mixed up as you remove children.

2 Likes