Closed reduz closed 2 years ago
I think the proposal is mostly good. I would most likely rename the "Make unique" thing, as it's already used to make resources unique and is thus a little bit confusing.
Maybe we could name it somehow like "Mark as path shortcut"? or somehow like that?
I'm not sure that giving a per-scene unique name to a node is the best solution here. It seems to me like the problem is that we want a node in the scene to have an easy reference to this particular node, without relying on find_node
or node paths. So we just need a way to get a reference to these nodes.
I think a much better idea would be to allow exporting Node references. So you could just have a field on the scene's root node and set the node that way, instead of depending on a unique name. This would be IMO much nicer and a lot less magic. This can kinda already be done with exporting NodePaths but it's not as nice. https://github.com/godotengine/godot-proposals/issues/1048
(EDIT: For anyone who says this isn't possible... Unity does it, so it is possible.)
I also don't like how this proposal makes scenes "exist" at runtime, in Godot scenes are groups of nodes and when instanced everything just becomes one scene with nodes in in the SceneTree.
I think a much better idea would be to allow exporting Node references. So you could just have a field on the scene's root node and set the node that way, instead of depending on a unique name. This would be IMO much nicer and a lot less magic. This can kinda already be done with exporting NodePaths but it's not as nice. https://github.com/godotengine/godot-proposals/issues/1048
I don't think this is possible or make sense, as node are accessed at runtime. A node cannot be "stored" in a scene, only its path. However I suppose what you mean is that doing something like RootNode.nodepath.call_something_on_the_node_itself()
would work.
https://github.com/godotengine/godot-proposals/issues/1048I also don't like how this proposal makes scenes "exist" at runtime, in Godot scenes are groups of nodes and when instanced everything just becomes one scene with nodes in in the SceneTree.
I don't think this is an issue at all. The "shortcuts" can be stored in the scene root node at runtime and would act like any other node.
Does this work with get_node()
and get_node_or_null()
?
as in can i do get_node("@Label1")
?
I really like groud's suggestion for handling this, and also that "Make unique" is already a bit loaded. "Make shortcut" also works in my mind, or "Set as shortcut". Actually, a nice feature would be if you called it "Set as shortcut", that could bring up a small dialog that you can type in what you want it to be aliased as in the script, and then it creates a reference to it for you in the script. That said, if there was no script to reference yet, it would be slightly annoying, so probably just not doing anything automatic like that is better.
This seems like a clever solution that avoids the boilerplate of adding and assigning to a NodePath export. My only feedback is about naming and syntax, really.
Using "Make Unique" here feels potentially confusing in light of what that means for resources and such. It almost feels as if it is implying that nodes are somehow shared across scenes by default and that this option undoes that sharing. "Shortcut" or "Alias" seem more along the right track to me.
Adding an @ into the nodepath syntax to reference one of these feels weird to me. Probably it would be too magical and error-prone, but what if the nodepath resolution just checked for these shortcuts/aliases first so that you could simply type $Label1
and it'd first check the list of unique/alias/shortcut nodes for an exact match and, if it can't find one, it falls back to resolving like normal? (On the other hand, a benefit of using a prefix like @
here is that once you mark the node as an alias in the editor, not only could the node's color change but the name's label could change, too, to include the prefix @
so it becomes more self-documenting.)
I don't see the need to create a new engine feature for unique node names. To my mind, the developer can just give the node a precise name and that should solve the problem for them.
I do see the benefit of offering a "find" operator within the node path. This would solve the issue of deep node paths, as well as the extreme pain of having to fixup node path references after moving something within the tree.
$DialogNode/@NameControl.text = "Hi"
The "@" symbol basically just means "first child whose name is NameControl, breadth first recursively"
Personally, I do not allow references to nodes using node path from outside the a scene. Refactoring node paths within the scene is already difficult enough. Any external node must use a method on the root node of the scene to act upon it, or the scene may respond to a signal from outside.
Cool suggestion imo. Maybe label it "Make Global to Scene"? Since you're giving "global" access to it within that scene. Sounds a bit more clear than "unique nodes" to me.
I just do
onready var foo = $path/to/foo
The advantage of this is that foo is just a field so everything just works. No special syntax nothing new to learn. It also works for parent nodes or sub-scenes.
I think that solving this in the UI by is better than adding it to the language.
@SilencedPerson
Does this work with get_node() and get_node_or_null()? as in can i do get_node("@Label1")?
Yes, Added the clarification. It should work with this fine.
@pchasco
I do see the benefit of offering a "find" operator within the node path.
Its not really a find operator, its simply a reference to a unique node within the scene. You get instant access to it, so its quite performant.
Maybe just every node exists should have unique id visible in inspector and i can copy it and use it from any where if it exists in the tree
@reduz
Its not really a find operator, its simply a reference to a unique node within the scene. You get instant access to it, so its quite performant.
For some reason I just have a little bit of a cringe reaction to the "make unique" feature because, in my mind, if the node is important enough to be referenced uniquely, then the developer should already be giving it a distinctive name.
I do see the benefits of having a shortcut to the node if that shortcut is preserved through the entire scene and is not lost when moving a node within the scene. Personally I just use onready variables for this and it's always been fine. I use find_node, but I don't worry about performance because the search is only done once and then always referenced via the variable afterwards.
I think a much better idea would be to allow exporting Node references
I agree with @aaronfranke
Using $Some/Long/Path syntax is error prone and should be avoided. It prevents developers from being able to freely adjust our scene trees. It means someone has to read script code before they can adjust node names or paths. If you want to change a VBox to an HBox, well now you have to read the code to see if the name is important. Want to move a button from one container to another? Read all the code first. Got a typo in a node name? search all in the project before changing it. It's more flimsy than using export(NodePath)
. For that reason, I think Godot should prefer to improve the usability of export(NodePath)
over the proposed solution.
Here are the two syntaxes in question:
onready var add_friend_button := $Some/Fragile/Long/Path/To/A/Button as BaseButton
vs
export(NodePath) var _add_friend_button
onready var add_friend_button := get_node(_add_friend_button) as BaseButton
Only in the second syntax is the add_friend_button
reference valid after renaming or moving the node. It also means that the script exposes its dependencies, so if a developer is creating a new friends UI, and they add this script to a node, they will see that it has a dependency on a node referred to by the handle add_friend_button
. That handle reveals the purpose of the node, and it also contains the Type. Godot can better support the typing of NodePaths, as well.
In my opinion, $Long/Node/Path is error prone, and this proposal is merely a syntax band aid to support poor workflow choices. The names of nodes should not be critical to the functionality of the scripts.
A better solution is to offer some syntax sugar to the 2 line export(NodePath) & onready syntax that is listed above. Here's my suggestion:
export(Label) var some_label
This could work by creating a shadow NodePath variable that is exported. The equivalent of this:
export(NodePath) var __some_label__
Then godot can add a implicit get_node to fetch some_label in the same system as onready
variables.
Godot should also allow type hints on NodePath exports like this:
export(NodePath, Label) var path_to_a_label
Just to add some clear contrast to the missing syntax sugar:
This godot code:
export(NodePath) var _label
onready var label := get_node(_label) as Label
Here's the equivalent of the above GDScript in Unity: (also note that the Unity code will only allow you to set Labels to the label field in the editor, godot doesnt support that)
public Label label;
Godot should solve the outlined workflow problem by adding syntax sugar to setting NodePath in the editor because it's a more powerful and useful paradigm. Every day I write dozens of export(NodePath)/onready pairs. This proposal wont change anything about my workflow and I won't use it. I'll still write dozens of export(NodePath)/onready pairs, and I'll still tell everyone who listens to me to do it that way.
Godot should solve the outlined workflow problem by adding syntax sugar to setting NodePath in the editor because it's a more powerful and useful paradigm.
Exporting a node from within a scene is already a code structure problem, as it breaks encapsulation of your scene. Better to have methods on the root of the scene that manipulate the children, and emit any signals from the scene root for external observers.
Godot should solve the outlined workflow problem by adding syntax sugar to setting NodePath in the editor because it's a more powerful and useful paradigm.
Exporting a node from within a scene is already a code structure problem, as it breaks encapsulation of your scene. Better to have methods on the root of the scene that manipulate the children, and emit any signals from the scene root for external observers.
I agree, my post is in alignment with that design. The syntax I recommend supports encapsulation of scenes, since by default the editor for setting NodePaths doesnt expose nodes not owned by the current scene, but using $Long/Path/Syntax does allow developers to touch things they shouldnt.
Does this work for 'editable children' sub-scenes, and if so what happens when a sub-scene tree content is changed and/or contains a naming/alias conflict ?
@aaronfranke
I also don't like how this proposal makes scenes "exist" at runtime, in Godot scenes are groups of nodes and when instanced everything just becomes one scene with nodes in in the SceneTree.
This makes me think that perhaps every Node could be able to store such shortcuts, but just happens that the editor UI allows you to place them on the root of the currently edited scene. That way scenes don't have to be considered an special thing at runtime.
I notice none of the example are reaching outward. I think the node path syntax would look for shortcuts on the Node it currently is at.
If I'm correct, the shortcuts places form the editor UI would only work on a script attached to root node of the scene. This might not be what developers expect. Edit: I suppose nodes could look for shortcuts in their owner as fallback.
I like @jasonwinterpixel idea above.
By the way, I have seen people do this (Godot 3.x):
export(NodePath) onready var add_friend_button = get_node(add_friend_button) as BaseButton
And Godot understand that. The variable is exported as a NodePath which you can set from the editor UI, and then on ready, Godot will get the Node it points to and store it on the same variable. And yes, the variable is not typed, and will have values of different types (it will be null in _init, a NodePath in _enter_tree and a BaseButton on _ready). And, by the way, Godot offers and autocompletes the members of the Node type.
For that reason, I think Godot should prefer to improve the usability of
export(NodePath)
over the proposed solution.
@jasonwinterpixel Those are not mutually exclusive features. Yes, we should allow exporting nodes directly, somehow. However, this proposal is an alternative to find_node
and other use cases where people rely on node paths in code. I prefer to have paths defined in code, because that is where all the work is done. I don't find it useful to me to export them and use UI to select or update the nodes. It's much faster to just write the correct path, it is autocompleted and all. And it allows me to be in one context when I work.
And yes, changing it is a maintenance, but I don't imagine a flow where someone is changing node composition of the scene, — nodes types and all, — and doesn't look at the code and doesn't change anything in code. This can only happen if you simply move the nodes, but such a change can be quickly reflected in code. And a change from HBox to VBox is not going to affect anything if you give your nodes names based on their semantic quality, which I always do when I organize scenes.
I'm not a fan of using find_node
, but some people do use it, and sometimes I have to work with such code. To me, this proposed solution would make code in these cases clearer (and faster!) without the need to move part of the logic to the editor's UI. And, again, I don't think it is competing with exposing nodes, a totally different case that we should also support.
If i understand this proposal correctly, this is basically adding a map of stringnames (unique key) to node references, into the scene definition/schema?
Yes, @pycbouh they are not mutually exclusive. But time is the great excluder and I am here to advocate for what I would prefer is prioritized.
I don't imagine a flow where someone is changing node composition of the scene, nodes types and all, and doesn't look at the code and doesn't change anything in code.
Large game development studios often employ more designers than developers. The way to enable designers to make changes to a scene is with export(NodePath).
Large game development studios often employ more designers than developers. The way to enable designers to make changes to a scene is with export(NodePath).
I'm not talking about the context. I really can't imagine what kind of changes can you do to a scene where you won't need to adjust your code somehow. It almost never comes to simply moving nodes around, when you run into a problem or a feature that requires changing a scene. It's almost always that you need to change the behavior as well.
And yes, changing it is a maintenance, but I don't imagine a flow where someone is changing node composition of the scene, — nodes types and all, — and doesn't look at the code and doesn't change anything in code. This can only happen if you simply move the nodes, but such a change can be quickly reflected in code. And a change from HBox to VBox is not going to affect anything if you give your nodes names based on their semantic quality, which I always do when I organize scenes.
This is commonly done when creating a pluggable scene.
I'm not a fan of using
find_node
, but some people do use it, and sometimes I have to work with such code. To me, this proposed solution would make code in these cases clearer without the need to move part of the logic to the editor's UI. And, again, I don't think it is competing with exposing nodes, a totally different case that we should also support.
The engine feature proposed can be easily solved by the developer by simply adding this line to their script, and using that to reference the node. Nodes of "importance" in a scene should probably be more formalized within the scene by creating variables anyway.
@onready var name_textbox = find_node("name_textbox")
I understand that creating a feature such as that proposed may make some people's lives a bit easier because they would not need to be as careful about node naming, but we should also consider that new features come with a cost. The cost must be paid both initially, and ongoing as long as the feature remains in the application. I wonder if the little bit of convenience for the developer is worth that cost.
Plus, as others have said, the "make unique" feature proposed is more of a developer's concern rather than a designer. Developer concerns should be kept in the code as much as possible.
I really can't imagine what kind of changes can you do to a scene where you won't need to adjust your code somehow. It almost never comes to simply moving nodes around, when you run into a problem or a feature that requires changing a scene.
I think this is really not representative of artists or designers who work within the context of a team in godot. This is exactly what designers do all the time, as they want to adjust layouts, alignments, an additional padding nodes or transform containers, etc, etc. They want to do this without having to understand gdscript or change code.
I think this is really not representative of artists or designers who work within the context of a team in godot. This is exactly what designers do all the time, as they want to adjust layouts, alignments, an additional padding nodes or transform containers, etc, etc. They want to do this without having to understand gdscript or change code.
Half of that is not done by moving any nodes, or breaking node paths. Again, I'm not against exporting nodes. I'm in favor of it, it's very useful. I just think that this proposal is also useful, because if we don't have this proposal, we are adding find_node
back, and that's really not great.
Half of that is not done by moving any nodes, or breaking node paths. Again, I'm not against exporting nodes. I'm in favor of it, it's very useful. I just think that this proposal is also useful, because if we don't have this proposal, we are adding
find_node
back, and that's really not great.
You are interpreting my one-off example list too literally, This is simply not true, moving any nodes, and breaking node paths
is exactly what it involves, very often.
The proposal has value, I'm just here to represent that I would prefer syntax sugar on export(NodePath) be prioritized higher than this feature, as I think export(NodePath) should be preferred over the alternatives.
I think I would like to understand a little more how the proposal is better than find_node
, other than the advantage or ability to remove a local var declaration. But perhaps that is the main one.
Presumably an @ lookup syntax still has runtime costs correct? It will need to figure out what subtree of a running scene tree (map) to lookup in and addition to a map lookup, rather than a DFS for find_node?
The proposal should elaborate more around how the proposal is better than find_node. find_node seems to be a more robust method to resolving a dynamic path at runtime.
My concern with the map lookup is how it is going to integrate with composable objects and scenes. Scripts that use the @ syntax, need to be fully aware of the local scene they are working in, but often I find it very useful to compose scenes which include subscenes with 'editable children'. This allows for more modularity when building reusable components.
So a question: Will the @ syntax work for referencing the SDF node from the script attached to say TankGame
(SDF is a node in a sub scene that is exposed with 'editable children'). From my understanding of the proposal, it would not work, but find_node would in this case. And if it does work in this case, how will it resolve alias conflicts or collisions?
@jasonwinterpixel
I'm just here to represent that I would prefer syntax sugar on export(NodePath) be prioritized higher than this feature, as I think export(NodePath) should be preferred over the alternatives.
I agree that also needs a better syntax sugar, but I think its a different problem and requires a different proposal. We did talk about this with @vnen.
This proposal will for sure solve the "get node hell" that most users has, here are my finger on this.
before I dig into other stuff, first I think the Make Unique
option is cool, and also a gdscript way should be implemented, like we have @tool
we can have something like @unique_node
or @node_alias
, also when you do via menu RMB if the node has a script the above tag will be added to the top of the script!
In terms of accessing the node the examples you provided, still produces a "semi get node hell ". A way to solve that is by having the unique nodes like a Singleton, example:
script A:
@unique_node uniqueNameHere
or @unique_node(uniqueNameHere)
script B:
var test := uniqueNameHere
or uniqueNameHere.something()
also uniqueNameHere.get_child("node")
or $uniqueNameHere/Label
via RMB after clicking Make Unique, a popup is shown by showing the unique name which is the node name, but the user can change!
I'd recommend not using "Make Unique" on the node since it conceptual collides with "Make Unique" on a resource. Perhaps this feature should be conceptually known as "Node Aliasing", and it should be "Make Alias" or something like that. Just something other than Make Unique
Does this make sense to expose at runtime though? It seems very editor oriented, and mappings end up being baked into the .tscn definition correct? Then LUT's are loaded at runtime into nodes that are the sub-tree 'owners' yes?
I guess I'm just curious if the syntax works with editable children, and if so how. As a concrete example, here's another screenshot from how we are currently doing, and customizing, a settings dialog. The dialog is composed of multiple sub-scenes, menu's, other dialogs, buttons, etc, and any special code logic happens in the scene root's script.
Right now in the above screenshot you can change the tree any which you we want because all the important node references in the script root are done as NodePaths which update. A good user-story of this occurring is when say we were modifying this scene, and some contents out-grew some container... The solution for this was to add a new node, ScrollContainer, somewhere mid scene tree. It was easily added mid-scene, the tree paths end up being totally different, and nothing breaks. And this also works with sub scenes with editable children.
So would just like to know if in the above screenshot I can do @NoButton
and @DebugSettings
in the UISettingsDislog
script, and it will work as expected. If @NoButton
works, is this something I make unique
in this scene, or in it's own scene instance?
@jordo For editable children it should work, like this:
$@SubScene1/@Label1.text = "Hello" # Still works
I copied the example from OP.
That is, you would have the path to the scene instance (which could very well have a unique name), and then you can use "/@"
to refer to a unique name inside the sub-scene.
@jasonwinterpixel good point, I just edited from make_unique
to unique_node
which I think that makes sense!
The advantages to doing things with NodePath references, especially in the editor, is anyone including designers can see what node ref dependencies any scene root class has. If I see a NodePath dependency called 'Log Out Button', it's intuitive at a glance if the dependency is hooked up or not. If it's done with aliasing or @syntax
, it's purely in code, there's no visual cue there, and it requires inspection of code to see if there is anything hooked up or if some ref is broken or disconnected.
There are some advantages doing this more with NodePaths, and leaning into a more NodePath oriented design or solution, because NodePath references already work with say renaming, they are kind of scene-oriented already, and there's a single hookup/failure spot. I can rename a node, and it's path reference automatically updates. I can also see, from the editor, with a quick glance if things are connected properly. With this proposal, can you rename an aliased/unique node? If so then any code syntax with @
will presumably break. The correlation and/or connection is 'hidden', and then someone will probably find that out at runtime.
Does the @
syntax from the node or target of invocation move up and down a scene tree? I.e., if I have a script attached to a node mid-way through a scene tree, does that @syntax
map to a node alias from a grandparent? And does this also work with editable children and sub scenes? Anyways, just lots of things to consider, potential edge cases, and I want to understand the rules on how the syntax works in different scenarios. Does it work prior to scene-tree enter, etc?
This is what currently works with NodePaths right now, it's pretty good! Moving, renaming, scene tree path modifications, dependency injection, etc.
https://user-images.githubusercontent.com/323868/155408872-dd0c0ca7-3d9e-4038-99fa-1b07bd1fe93e.mp4
Basically just saying that, if you have a script that does something like the example in the OP proposal:
$@SubScene1/@Label1.text = "Hello" # Still works
# Instead of
$Path/To/SubScene1/VBoxContainer/HBoxContainer/Label1.text = "Hello"
What that means is that your script has a dependency on
$@SubScene1/@Label1
existing, and being resolved, as it's going to be writing a value of "Hello" to the .text
property. If that dependency isn't resolved, (maybe you forgot to make a node 'unqiue', or maybe you have a typo, etc), things will break.
The proposal is OK, but sometimes not adding more ways to do things is OK too. NodePaths are nice right now because they auto-complete, there's a nice dialog picker for the editor etc. So I also kind of agree that a proposal surrounding improving the NodePath dependency/requirement can potentially improve what this proposal is also trying to improve. It seems somewhat related and definitely not mutually exclusive to me. It basically is a way to reliably reference a dependent node from a base script, with the minimal amount of effort and chance to misconfigure. But this proposal still requires multiple steps. 1) Write code that contains $@TargetNode
syntax. 2) Make sure there is a node named TargetNode
in the current scene (is there editor autocomplete with this?), and 3) make sure the TargetNode
is configured to be a unique node. If any of those things aren't typed right, it won't work and you won't find out until runtime.
I think syntactic sugar around around node/nodepath dependencies can potentially solve the same problem. Say
@Required RequiredLabel1 : Label
Then you can click/choose it from the editor (editor can open up a scene tree node chooser, much like how NodePaths work right now), and you can just select the label you want wherever it is. There's no chance for typo error, as the connection is explicit and visual, and in your code you can still type:
RequiredLabel1.text = "Hello"
And the hookup is still in the editor, although just with the tree's node chooser dialog, instead of an explicit Node name + RMB + make unique click.
Or even better yet, after typing @Required
in the gdscript editor window, the editor could prompt you to select a target node from the current scene (much how the current autocomplete for typing NodePath's work) to set this reference to. After selection it could even auto fill the type as well, which would result in code that looks like:
@Required RequiredLabel1 : Label
Could work much how the current autocomplete for typing NodePath's work, except you are declaring a node reference (which could be backed by a NodePath as an implementation detail).
Would be cool to see something along these lines, maybe with @Required
syntax, or @Optional
syntax, which could convey correct operation if a dependency is unset / unconnected.
I like the idea of @required, but would extend this to the idea of exports in general and not only limited to nodepath. If a value is not supplied then the project should not compile. If compile-time validation is not feasible, then a check at runtime to ensure all @required exports are satisfied at the end of the ready event might be OK.
The original proposal adds additional complexity to the editor and engine to solve a problem that already has at least two solutions: Use an exported nodepath variable, or assign an @onready variable with find_node with appropriately named nodes. The cost to find_node is trivial if it is only being called at ready.
@jordo @pchasco How about @required
generating a node configuration warning instead (the warning that appears as a yellow triangle next to the node in the scene editor), which is a feature that already exists.
Perhaps it can be expanded so that it does not only shows warnings but also errors. And if there are errors, have a prompt asking the user to confirm if they want to build the project anyway.
However, I think these should be two separate proposals: one for @required
, and one for node configuration errors.
@theraot That seems decent to me. We should obey conventions whenever possible.
The original proposal adds additional complexity to the editor and engine to solve a problem that already has at least two solutions: Use an exported nodepath variable, or assign an
@onready
variable with find_node with appropriately named nodes. The cost to find_node is trivial if it is only being called at ready.
find_node
has been removed in master. So there is currently no solution for non-designers. What reduz is proposing is a solution that would fill find_node
's shoes better instead of restoring that potentially misused method.
Let's please stay on topic. Export validation is an interesting idea, but it deserves its own proposal instead of taking time from discussing the feature proposed here.
It seems to me this is really about performance, as the node would still be addressed by its name and i'd consider $@Label1
not more readable or writable than find_node("Label1")
.
If that's correct, couldn't this just be a compile time optimization? As in "every find_node(x) is replaced with a direct reference to x, if x is already present below the calling node"?
@pycbouh I wasn't aware find_node was removed. That's a bummer. Find_node was the only thing that made working with nodes tolerable. I wonder if removing the find_node tool was not the correct decision since we're now looking for ways to get the functionality back without calling it find_node?
If that's correct, couldn't this just be a compile time optimization? As in "every find_node(x) is replaced with a direct reference to x, if x is already present below the calling node"?
find_node
accepts any string input. As such, the nodes it not guaranteed to be there at "compile" time. It can also be a variable. This cannot be optimized, it's purpose is to do a full tree lookup. Which is why it was a long standing desire to remove the method, and so it was done recently.
@pycbouh I wasn't aware find_node was removed. That's a bummer. Find_node was the only thing that made working with nodes tolerable. I wonder if removing the find_node tool was not the correct decision since we're now looking for ways to get the functionality back without calling it find_node?
It wasn't completely removed, it was replaced with a more general purpose and versatile find_nodes()
(see https://github.com/godotengine/godot/pull/56032). Which in turn made it ugly to use for onready node references (see https://github.com/godotengine/godot/issues/58412).
As we were discussing the restoration, reduz proposed this alternative. It would be desirable to not introduce a potentially dangerous and inefficient method, that is so easily misused. So yeah, we do want to have this functionality without actually doing the same thing internally and implement it smarter instead.
This is the full context for this proposal to happen, I'm not sure why reduz didn't mention it in the OP.
I think the proposal is mostly good. I would most likely rename the "Make unique" thing, as it's already used to make resources unique and is thus a little bit confusing.
Maybe we could name it somehow like "Mark as path shortcut"? or somehow like that?
Was about to mention the very same thing. This feels potentially very misleading. How about calling it Manage Alias
instead?
However, the more I think about it, the more likely this might be better suited as a new pair of annotations? That's clearly not necessarily as fancy as some menu item and a dialog box, but then again these names would mostly (only?) be used in scripts anyway, so might make more sense to put this there.
Also they wouldn't have to be unique and instead find the first matching element (which might be useful in some situations, too).
# Any child node may access this node under this name (move up until a matching alias is found)
@local var something_here = ...
# Any child or parent node may access this node under this name (move up, if nothing is found, move into children, their siblings, then grandchildren?)
@global var something_there = ...
# Make the current node accessible
@global @onready var PLAYER = self
Having quite a large project in Godot, I don't see this proposal as something we would need or use.
What might be useful is a globally unique name so I can access get_node("@node")
in any loaded scene anywhere (attached to the tree or not). But locally unique doesn't provide any clear benefit above what is already provided.
We already specify nodes in our scripts. Changing this when we update the path is simple.
onready var button = $Control/HBox/Button
...
button.text = "Submit"
And we occasionally use find_node when the structure is dynamic. Globally unique names might help this, as well as other uses I can think of in our in game scene loader and scene management. But a locally unique name doesn't do anything helpful for us.
Also it shouldn't be called "make unique". That phrase is taken. I would call it "make global" given my described functionality. For the current proposed functionality, someone's idea of "make alias" is fine.
Also what would be useful are unique resource identifiers in scene files (instead of incremented id numbers) so they can be mergeable with git and allow multiple people to edit scene files. @reduz you mentioned something about this months ago. Was this proposed or implemented?
@tinmanjuggernaut Globally unique nodes already exist in the form of AutoLoads and can be accessed with $"/root/MyNode"
.
I do see the benefit of offering a "find" operator within the node path. This would solve the issue of deep node paths, as well as the extreme pain of having to fixup node path references after moving something within the tree.
$DialogNode/@NameControl.text = "Hi"
How about using other path
syntax forms users might be familiar with?
*
Placeholder for a name or part of a name, one or more characters**
Placeholder for a path, one or multiple layers deep# find first node anywhere
var something = $/**/something
# find first direct child
var something = $*
# same
var something = $**
# find first direct child with a name starting with `a`
var something = $a*
# find any child with a name starting with `a`
var something =$**/a*
# find first direct child with a child `label`
var something = $*/label
# find any child with a child `label`
var something = $**/label
Not a bad idea as a whole, but please don't convert GDScript to Perl with these $@
things.
How about one gives a node an alias, and under this alias it auto-registers itself equivalent to @onready var Alias = ....
, but without actually adding this code in the text file?
We have visual connection of node signals to functions already (and they are not out of place because one side, the scene tree, is visual), why not also have visual connection of nodes to variables?
EDIT: To @aaronfranke below, I'm not referring to anything global to the game, please take a look at the signal connection UI, if you haven't used it before, that's what I'm referring to. In the script the glue code is absent, it's only shown in the form of a mark near the line number. This allows to not add new operators like @
, as it doesn't matter if the equivalent full code to make the connection is long ("connect(....)") or not.
Describe the project you are working on
Godot
Describe the problem or limitation you are having in your project
Currently in Godot, finding specific nodes that have different accesses across the project is done the following way:
get_node("/root/name")
or as a global variable in GDScript.$SomeNode/ChildOfIt/IwantThisOne
This suffices for a lot of cases, but it often happens that developers want to access a specific node in the scene tree, but this node is situated behind layers of containers or other parent nodes, so typing the full path such as:
$VBoxContainer/HBoxContainer/VBoxContainer/Option1/Label1.text = " hello"
Can be very cumbersome and annoying, even with code completion.
What many users do to work around this is to put a unique name to it and use find_node with an autoload to find it:
@onready var label1 : Label = find_node("Label1")
But this is messy. If there are many nodes around, having to create local variables for each makes code difficult to read. Also the solution is just not obvious.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
The way to solve this is by implementing Scene Unique Nodes.
These nodes would only exist once per scene and you will be able to access them by name by ensuring their name is unique.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
The idea is like this:
RMB any node and set it as unique:
This node will be, thus, unique in this local scene. It does not mean that any other node can't be named the same, but it ensures that no unique nodes can't be named the same:
Then, accessing it from script would be simply, just type the name:
And that's it.
So. some questions may arise such as:
Q: What is the difference with find? A: More strict and largely more performant, as access will be instant because this node is unique (no actual searching takes place). Simpler to type and to understand when reading the code.
Q: What if I want to make the parent unique but access a child node? A:
$@Label1/Child.say_hello()
Q: What if I want to access it in a sub-scene? A: The idea is that here we want to satisfy the most common case which is searching within a scene, but you can work around this rather easily. If the scene in the example above was a subecene you could do:
But additionally, you can make the subscene also a scene unique node, so you can do:
Q: What about non GDScript languages? A:
get_node
(GetNode
in C#) will work the same:Thanks @pycbouh and @groud for giving feedback on how to implement this to make this proposal more solid.
If this enhancement will not be used often, can it be worked around with a few lines of script?
N/A
Is there a reason why this should be core and not an add-on in the asset library?
N/A