It'd be really nice to have a function that loops through all children of a node and returns all of its children and their children recursively, infinitely (until all children have been traversed).
For my use case, I'm looking for a specific node, and to do that, I have to look through all present in a scene to find the one with the specific property I'm looking for.
I'm surprised this isn't in the API already, unless I'm just missing something.
Is find_node not sufficient? Love your stream by the way.
For my use case, I'm looking for a specific node, and to do that, I have to look through all present in a scene to find the one with the specific property I'm looking for.
I wonder why you'd need to do this, sounds like it is very inefficient. For this it would be better to at least have it as an iterator so it's not needed to fill the whole array beforehand and allow you to get early out of the loop without checking every node (still not that efficient though).
OTOH this might be useful for plugins or general tools, that can't know the state of the tree in advance.
OTOH this might be useful for plugins or general tools, that can't know the state of the tree in advance.
There's now a signal on SceneTree called node_added for that now. If the plugin can be initialized before the scene tree it can be notified every time a node is added, including all of it's children.
This could possibly be used for the problem @SolarLune is having by using an autoload? I'm not sure.
I wonder why you'd need to do this, sounds like it is very inefficient.
For my purposes, I'm making a top-down 2D game, and I wish to have exits that lead from one scene to another. I've decided to go with an ID / coding solution for pairing exits between scenes. This means that one exit of ID 12 would lead to the exit with an ID of 12 in the target scene. In order to see which exits are tied together, I need to load the target scene and look through its nodes to find an exit with the appropriate paired ID.
Efficiency isn't really too much of a concern at this stage. Looping over a few hundred nodes should be no problem for Godot, much less just a few (which is my case). Even if it was, I'll worry about efficiency once I have it actually working, haha.
As for other use cases, it doesn't seem out of the ordinary to have a need to loop through children and grandchildren of a node.
Is find_node not sufficient? Love your stream by the way.
Nope! There can be more than one exit in a room, so I'd need a way to find all exits in order to filter that list down to an appropriate one. And thanks!
Just thinking out loud, I don't know your setup... Is it not a better idea to have a script on the exits and have them walk their way up and notify a common parent controller?
Not sure what you mean - can you explain a bit further?
What do you mean by "parent controller", and what is it common to? The exit nodes are in different scenes, so there's nothing really common between them except existing in the same scene tree. What should the exits notify the controller of? I would just be using them as position information to indicate where exits lead, so they're passive, in a sense.
How about putting nodes with the same ID in the same group? http://docs.godotengine.org/en/latest/classes/class_node.html?#class-node-add-to-group
Doing so would break the object oriented design pattern : a child node is basically a private component of its parent, so its parent's parent should not be able to reach it. Also, this is completely inefficient.
Instead, and as Noshyaar suggests, it would be much more accurate to put them into a group.
A parent's parent can easily reach a child node, that's the entire concept of the scene tree.
Find node already performs a recursive search, it wouldn't exactly be that much more inefficient to just return all the nodes.
I agree there's probably a better way around doing this that doesn't involve recursion but the functionality itself doesn't seem like a problem to me.
Find node already performs a recursive search, it wouldn't exactly be that much more inefficient to just return all the nodes.
Well, find_node is already not considered a good solution, it's already kind of a workaround. So, yeah, we can implement such a get_children_recursive() function, but as it is an inherently inefficient design I would prefer not so. That would avoid people using such workaround and complaining about it being slow.
Also, such behaviour is perfectly writeable with a few lines of scripts. So IMHO, it's not worth adding to the core.
Also, such behaviour is perfectly writeable with a few lines of scripts. So IMHO, it's not worth adding to the core.
The same can be said for get_children. Having it in the core allows it to be more performant.
What do you mean by "parent controller", and what is it common to?
Sure, so your hiearchy of nodes might look something like this (but likely much deeper):
It wouldn't have to be called ExitController, it might just be a more generic GameController or something like that, but for clarity I'm calling it ExitController.
Pardon the untested code... So, in the GDScript the Exit.gd might look like:
# Exit.gd
@export var id = 0
var exit_controller
func _ready():
var parent = self.get_parent()
while parent != null:
if parent.get_name() == "ExitController":
exit_controller = parent.get_name()
break
else:
parent = parent.get_parent()
exit_controller.call("add_exit", self)
func _exit_tree():
exit_controller.call("remove_exit", self)
This way it only goes up the hierarchy and only checks a few parents which would be very fast instead of checking each and every child which might be much slower.
ExitController.gd would implement "add_exit" and "remove_exit" which would append and remove elements from an array of exits. There would obviously be quite a bit more code for the entire Exit system. For instance, you'll probably want to check whether the Exit was triggered in Exit.gd, but then inform ExitController.gd and let it handle what happens when that exit is triggered.
I'm not sure if that answers your problem, but I hope it helps :)
The same can be said for get_children. Having it in the core allows it to be more performant.
You mean the get_children() function ? If so, this function does not break the object oriented design, and is thus a lot more useful than a recursive version.
Just have a look to the code of the editor, the find_node() function is never used, which kind of proves it's corner case usage. Honestly, I don't see the use of such get_recursive_children(), and would remove find_node() from the core if it was up to me. For the remaining, I just follow this diagram: :)
https://twitter.com/reduzio/status/941799197774315525
Hello! I finally got around to trying this:
How about putting nodes with the same ID in the same group?
And it works fine. Thanks!
That would avoid people using such workaround and complaining about it being slow.
That's, in my opinion, not a good reason to avoid putting a functionality in a game engine. People will complain about their code being slow when it's poorly optimized and they don't understand why. You can remove all "inefficient" functions, but people will still end up with slow code if they write it. Basically, just document that its usage might indicate an inefficient approach to a problem, and to use it sparingly.
I don't particularly need getChildrenRecursive anymore, though, so this is fine. Thanks, everybody.
Most helpful comment
The same can be said for get_children. Having it in the core allows it to be more performant.