Godot Unique Scene Names – New to 3.5/4.0
Godot 3.5 (and seemingly originally developed for Godot 4.0) is getting a new feature that allows you to access a node using its name in the scene instead of its node path. This is a potentially welcome addition, especially for UI development but I’m not sure it’s the ultimate solution, let’s take a look.
Currently to access a node from a script you type get_node(“path/to/node”) or $path/to/node for brevity. This CAN work for objects higher up in the scene tree by using ../ but it gets messy fast. The problem with this approach is that with complex scenes things can get very messy and path strings can get very long. This becomes especially problematic if you need to rearrange your scene structure. Take the following example.
Game
Player
Enemies
Enemy1
Child Node
If you need to access the child node of Enemy1 from Game you would type $Enemies/Enemy1/Child Node. Now, imagine later on you realise you need to structure your game so that each enemy type is in their own parent node the structure would now become:
Game
Player
Enemies
EnemyType1
Enemy1
Child Node
EnemyType2
Now anywhere where you need to reference Enemy1 or its child needs to be updated. Hopefully you have structured your code in such a way that this reference only occurs a few times but it will still take some tracking down and manual editing and in all but the simplest projects, you are likely to miss a few until you get a dreaded debug stop.
So the latest proposed design allows you to reference a specific node from anywhere in its current scene. The way you would do this is by right clicking on the node and selecting the “Unique Scene Name” setting.
Let’s say you apply this flag to Enemy1 you can now access Enemy1 from anywhere in the current scene using $”%Enemy1″. To access the child node you can also use $”%Enemy1/Child Node”. If you move Enemy1 outside of Enemies or under a new node you will still be able to access it using the % unique name syntax.
A caveat to this new approach is that it only makes the node name unique to the lowest level scene that it is a part of. Therefore if you make a node structure where one child has Unique Scene Name enabled and then make its parent a scene of its own, the child will now only have an unqiue name in instances of its parent scene and not the top level scene, which you probably don’t want. An example of this is making some nodes as part of a level and realising you would like to reuse this node structure elsewhere in the game, say a button triggered trap, once the new scene has been created only the root node will be able to use the unique scene name for the child.
I don’t really see this as a problem, however, as I’m not sure why you would want to make a node that is only useful as part of an instanced scene (a component node) accessible from anywhere within the top level scene, more likely you would give the instanced scene (character, weapon etc) a method to manipulate the component (or at least provide access to it). You could argue that this increases the need for systems to know the structure of other systems but I’m not sure that’s a problem in most cases. If you structure your code and scenes well it won’t reduce the princple of single responsibility or other similar principles. You likely only want key objects/nodes that are accessed by lots of systems (such as your player or a specific boss or location or UI) to have unique scene names. Other items would probably be best found through their parents or systems designed specifically to find nodes of a specific type. I can see this being a problem in the case given, where a node becomes a part of the instanced scene but this should be fixable and addressed when the node is first converted to a scene and shouldn’t be an ongoing issue. As explained above, if you have made the parent a scene, it likely means the scene should be the part you care most about.
Another caveat is that there can only be one node in the entire scene with the same unique name.
Game
FlyingEnemies
Enemy1
GroundEnemies
Enemy1
Even though both Enemy1 nodes are in different parents only one can be marked as having an unique scene name. While this isn’t a problem or likely to come up too often AND is fairly easy to fix (just rename one), it is a limitation worth knowing about.
Now while I like this addition to Godot on paper (I haven’t tried 3.5 extensively yet, just a quick test of the web editor while writing this article\), I think this is only a partial solution. If you rename the node, you still need to update the references, it just prevents having to update nested node paths when you simply move an object in the scene. In Godot, each object in the game already has a unique resource/object ID and while this is generated at instance time (and seems to change every time the game is run), I don’t see a reason why a specific object couldn’t be set to have a “fixed” unique ID, stored in the editor or elsewhere that allows you to access a specific object no matter where it is in the node tree, child of an instance or otherwise. This could always be converted to a NodePath at runtime or even the object reference itself. It could even be that these unique IDs are ONLY stored if the object has the unique ID flag set, thus saving resources for the editor where it only has to convert and keep track of the objects you want to use with this feature. I suspect this might not be as performant as Unique Scene Name or the other options currently available (explained below) but I’m not intimately familiar with the internals of the engine despite poking around in the source from time to time, so take this suggestion with due care (though I do think this comment may be referring to a similar solution).
Other current solutions (Godot 3.4) to accessing a node from anywhere in the scene tree include giving each node a group then finding that node by name from within the group, exporting NodePaths and manually assigning them in the editor or using onready var. Each of these has their own drawbacks and I’d say I personally use them almost equally, though I favour onready var and exported NodePaths.
Using groups you can do things like:
if $some_path.is_in_group("group_name"):
# Do the thing
for obj in get_tree().get_nodes_in_group("group_name"):
if obj.name == "Node I'm looking for" or obj is KinematicBody2D:
# Do the thing
# Or even something like this
for obj in get_tree().get_nodes_in_group("top_level_group"): # enemies
if obj.is_in_group("group_name_specific_to_only_one_node"): # specific enemy that triggers when an area is entered
# Do the thing
Godot has an export variable. This works similar to how variables work in Unity, exposing (exporting) them to be edited in the editor. One of the built in types is the NodePath export. This allows you to drag and drop a reference to a node anywhere in the scene to the inspector fo the currently selected node. This reference will be updated (for the most part) through any adjustment to the position of the objects within the scene tree. I find this solution good for connecting two objects in the scene that need to know about each other but whose position in the tree may not be determined consistently (and on ocassion, just for connecting known fixed nodes without having to write lots of manual onready vars (more on this below) or get_node()s. The biggest issue with this is remembering to connect them in the editor and then doing the relevant sanity checking in code and sometimes having the connection break because of a bug or severe change to the scene tree that can’t be tracked automatically by the editor.
export (NodePath) var some_node_path # e.g. ../my/fancy/node
onready var some_node = get_node(some_node_path)
func _ready():
if some_node:
# Huzzah!!
pass
func _process(delta):
if some_node:
some.node.do_the_thing()
Using onready var you would do something like this:
onready var enemy1 = $path/to/enemy1
You then use references to the enemy1 variable anywhere you would call $Path/To/Enemy1. If you need to move the node, you can just quickly update the onready var path and get back to what you were doing. I like this solution but depending on the complexity of your inter-node functionality this still may require updating many, many scripts. It also really only works for specific UI elements and “top level” group nodes rather than individual entities like enemies.
The final current solution is to create an autoload node. This node will be loaded at game initialisation and will be accessible from the scene tree root or from the global variable name assigned to it. This is mainly useful for global systems that need to be accessed by large parts of the game such as inventory management or score tracking. I don’t believe it’s advised to rely on autoloads for large parts of your game.
All this to say I’m intrigued by this new feature, but to me, this seems like it would be most useful for UI systems where the layout/structure may change during developement and where many different systems are trying to read/update values in the UI Control nodes, thus having references to these nodes. While some of these issues could be solved by using signals, the problem still remains that the connections either have to be made in code, usually in a more top level script or they have to be connected in the editor and signals won’t be performant/useful for all cases where the UI needs to be accessed/manipulated.