Is it safe to `add_child()` to a node while iterating through its `get_children()` array?

Godot Version

4.2.1

Question

Is the following code safe?

for existing_node in self.get_children():
    var new_node = existing_node.duplicate()
    new_node.rotate = PI
    self.add_child( new_node )

Where safe means:

  • it won’t crash because the array in the self node gets recreated due to extension.
  • the for loop will only see the nodes that existed just prior to for loop executing; it will not see the nodes it adds itself.

Added children will appear in the tree only on the next frame, so your for loop is unaffected.

1 Like

It is not clear from the forum UI, but it looks like you were replying to me instead of @anon45973056.

If you were adding to @anon45973056 response, then what you said is true.
If you were replying to @darknova, then your statement is not true.

The below shows that after is equal to before + 1:

func _ready():
    var before = $SomeNode.get_child_count()
    $SomeNode.add_child( new_node )
    var after = $SomeNode.get_child_count()
    print( before, " == ", after )

@anon45973056 solution works.
But so does this:

var children = self.get_children():
for existing_node in children:
    var new_node = existing_node.duplicate()
    new_node.rotate = PI
    self.add_child( new_node )

In this case the array stored in var children is a shallow copy of the self’s actual array. So it doesn’t change while self’s original copy has children added.

I suspect that this is the case with the original for-loop, too, because it is probably get_children itself that does the shallow copy, but it is unclear from the documentation as to whether that is true.

Or an index value

for i in get_child_count(): #same as for i in range(get_child_count()):
    var a_copy = get_child(i).duplicate()
    a_copy.rotation += PI
    add_child(a_copy)

You’re always on thin ice playing with the thing you’re currently looping through though.

2 Likes

You’re always on thin ice playing with the thing you’re currently looping through though.

True, but I’ve proven that that’s not what happening here. There are two arrays:

  • the one I’m looping over in children
  • the one I’m adding to using self.add_child

So it should be fine.

You’re good I was dunking on my own code. I actually do it the way posted whenever I need to mess with the children during the loop, easier just to not do anything potentially weird.

My personal favorite is doing a reverse loop so I can delete items like:

for i in reverse_range(len(arr)): #reverse_range -> range(x-1, -1, -1)
    if condition(arr[i]):
        arr.remove_at(i)
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.