Open Mickeon opened 2 years ago
This proposal has been a long time brewing in my head. I mustered some courage proposing this potentially highly controversial change thanks to the Godot community on Discord.
This proposal depends on #5048's implementation, to be an even more justifiable addition.
I'm personally for this change, I agree with %
being a bit overused, and I don't like that there's 3 different syntaxes for accessing unique nodes; $%
, %
, and get_node("%")
. I'm definitely not for adding $$
though if the other syntax options are kept. Since 3.5 is almost out, I'd say leave the feature as is in 3.x so 3.5 doesn't have to get delayed any longer, and this could be added to the Godot 3 -> 4 project converter.
I also don't like how overused % is and how many different proper syntaxes there are for scene-unique nodes. Though the more I think of it, the more I approve of it. I'm still in support of adding get_unique_node()
, but let me go through my long line of reasoning.
get_node()
. "%" can be avoided by chaining a get_unique_node()
instead. If the mentioned proposal gets implemented, though.But the highlighting idea...
get_node()
syntax as the argument is a homogeneous yellow string.strongly for both get_unique_node()
and $$
as that would allow to avoid introducing yet another application of %
entirely and make for much more uniform and straight forward to understand usage,
where $
matches get_node()
and $$
matches get_unique_node()
.
four ways to do the same thing is a mess.
%Node
$%Node
$"%Node"
get_node("%Node")
$
is already a well established shorthand for getting a node that doesn't conflict with anything and everybody understands.
utilizing it again makes much more sense than introducing something entirely new, but $%Node
physically damages my eyes and inflicts a bleeding debuff. $$Node
looks ok to me, but my favorite for readability remains $*Node
as originally proposed by @AndersonDeMatos in the PR. in the context it would be used, I don't think there's any chance of confusing it for a multiplication operator.
some contributors in the other proposal were saying that this has already been discussed and we are too late for this, but most of this has actually been proposed early on in the PR and got a decent amount of approval, so what are we supposed to do... the notion that we should wait for strong community backlash before changing it is strange too. of course there's not going to be strong feedback from many users, after all it's not like the feature is unusable. but that's no reason not to clean it up before stable.
btw, in the last line of your screenshot example, $$Mango/Seed
should still work just the same.
lastly a mention on the side, if the popular #1776 were to be implemented, chaining like that would become possble: $$UniqueNode/DirectChild.$$UniqueNode/AnotherChild
even further reducing the use-case of having unique node accessors in the middle of node paths.
four ways to do the same thing is a mess.
%Node
$%Node
$"%Node"
get_node("%Node")
Five, there is also get_node(^"%Node")
.
^
denotes a NodePath, which since recently has a different color than regular String. Using it in get_node()
gives 5x performance gain (last time I checked).
lastly a mention on the side, if the popular #1776 were to be implemented, chaining like that would become possble:
$$UniqueNode/DirectChild.$$UniqueNode/AnotherChild
Indeed. I was afraid of mentioning this proposal, to avoid going too off-topic, but to me personally, that syntax does look much less headache-inducing than the current sequences of %s.
Five, there is also
get_node(^"%Node")
.
Time to add it to the pile...
Note that this proposal conflicts with #996 and I find $ for normal and $$ for caching more intuitive than $ for normal, $$ for unique and... what? for caching...
Scene Unique Nodes are inherently cached, so in a way, that very proposal would be implemented if this one is, but in a way that is more integrated in the Engine. I do not see any conflict.
@Mickeon I don't want to have to use unique nodes to benefit from caching. And the obvious conflict is, you can't use $$ for both (non-unique) caching and unique nodes.
If it's too much of an issue, storing Node references in variables is always a perfectly viable option, it's typically recommended and results in cleaner code, too.
@Mickeon Unless you want the scene to have no script
perhaps integrating caching as proposed by #996 into scene unique nodes is the only way we realistically get it at all, judging by the critique brought forth by groud, vnen, and Zylann in that proposal.
btw, although reduz mentions "Implementation is very optimal, as these nodes are cached." in the unique scene PR, this is not the caching proposed by #996, as performance of $
and %
is currently identical. if scene unique nodes would implement #996, then performance of %
should be much closer to onready declarations.
Note that this proposal conflicts with #996 and I find $ for normal and $$ for caching more intuitive than $ for normal, $$ for unique and... what? for caching...
well I was thinking here, maybe remove the "%" since its used for strings like: "%s" % "hello"
then we could use special chars:
# $_ get node
var s = $_myNode
# $U_ get unique node
var s = $U_myNode
# $C_ cache node
var s = $C_myNode
This convention would heavily go against the first point brought up in the proposal.
I'm not a fan of $$
the repeating doesn't serve a logical/semantic purpose, and it could also be harder for some people to make out when quickly skimming over code.
Keep in mind I am not a fan of the current %
either, as stated in the post it already has full fledged operator use cases.
var my_node: Node = $NodePath
var my_node: Node = $NodePath/Stuff
var my_node: Node = $"NodePath/With Spaces and stuff"
var my_node: Node = @UniqueName
var my_node: Node = @"UniqueName/With Spaces and stuff"
var my_node: Node = get_node("Path/Name")
Idea.
I would like to say that @ is not used anywhere in gdscript, and that it makes sense here "at". However after just checking, the above example of ^ to indicate a nodepath is apparently wrong and it is in fact @ .
From the docs: @"Node/Label" = NodePath or StringName
But my point stands.
Use a unique symbol that isn't used elsewhere in the language. And merge it with the get_node $ call.
And don't support unique names in get_node because it's pointless.
I would like to say that @ is not used anywhere in gdscript
It's used for annotations. It was NodePath in 3.x, but in 4.0 NodePaths are ^
.
Yeah, sorry. As much sense as that makes, "@" has been taken. Because of their rather unique purpose, either the annotation's symbol is changed to make space for that suggestion, or this is still up to debate.
And don't support unique names in get_node because it's pointless.
As of currently, it seems like the support is not just within get_node()
. Rather, The "%", so to speak, are very much part of the NodePath itself. As I brought up in the proposal:
This proposal also heavily suggests to outright remove the "%" as a Scene Unique Node accessor. It doesn't seem mandatory, as both could coexist, despite the spoiling of choice it would cause. By removing it, long, mixed NodePaths such as
$%UniqueNode/DirectChild/%UniqueNode/AnotherChild
would no longer be possible, and other languages may especially struggle fetching highly-nested Nodes. Although, it's really worth debating the usefulness of this somewhere else.
Also, bit funny how this goes:
In the proposal I confidently called $%UniqueNode/DirectChild/%UniqueNode/AnotherChild
a NodePath. This is technically wrong, as because of "$", that would be a Node referenced by path. The NodePath equivalent would actually be ^"%UniqueNode/DirectChild/%UniqueNode/AnotherChild"
(in Godot 4.0).
It's a really small mistake, it's true, but it may partially be attributed to how "littered" this current syntax feels.
From the docs: @"Node/Label" = NodePath or StringName
make sure to check the latest docs. just swap "stable" for "latest" in the URL.
Use a unique symbol that isn't used elsewhere in the language.
sure, but easier said than done. the currently chosen %
was already one of the few possible contenders despite its numerous existing uses. that's why the combination of two characters is an interesting option, especially since we already do have our get_node
character; $
. it feels logical and intuitive to me to keep using $
with an additional modifier character to indicate that we want to use a special flavor of get_node()
.
And merge it with the get_node $ call.
not sure what you mean exactly, can you give an example?
I did give a example.
Use $ or get_node() to try and get a node by it's Path. (This path my be cast from a string.)
Use @, which is now actually free to use then (excepts it's used in export notation), or another symbol to reference a unique node.
Make it § for all I care.
Again:
var my_node: Node = $NodePath # Calls get_node() with string/nodepath.
var my_node: Node = @NodeName # "Doesn't call anything", gets the unique node directly.
excepts it's used in export notation
which is probably enough to exclude @
from being an option.
this is just not going to pass:
@export var my_node = @MyNode
even %
is more suitable, as before unique nodes, %
was never used at the beginning of a line / expression.
Make it § for all I care.
looks like you are sitting on a German keyboard, too. check other layouts though, it's not on there.
looks like you are sitting on a German keyboard, too. check other layouts though, it's not on there.
No... it's present on the british and us international standard layout.
In fact, we could just as well use ° which is also present on both.
It doesn't matter. My feedback is much less so about the symbol. But about it's function.
Outside of a string path. Not inside.
You guys should've just changed how $
works, its purpose is to be a shortcut right? Well in that case it should've just been adjusted to fit its purpose more. ($
should've been what $%
is now)
get_node()
still exists after all.
My feedback is much less so about the symbol. But about it's function.
Outside of a string path. Not inside.
%
is part of NodePath
, just like ..
. As mentioned above, you can use %
multiple times in a path to find unique nodes in multiple nested scenes, skipping the rest of the nodes (unique nodes are associated with the owner
property).
Accordingly, even if we replace the %
character with some other one, it will still be part of the path.
Personally, I don't see the need to replace %
with something else. The %
in a string is a bit like the start of a substitution, but I don't really think it's a big deal. My vote is for leaving things as they are. $$Node
does not have any significant advantages over %Node
.
Hm, @ does indeed make a lot of sense. I guess using it for annotations is a resemblance to Python, but we could really have it do double duty when the uses are so clearly different (a node path does NOT look like an annotation)
Just my two cents, potential operator changes that I wouldn't mind are ! and &. Personally, I lean more towards &. I do only prefer this under the circumstance that it would be used as a standalone operator and not in conjunction with $, though I'm not actually a big fan of that idea since it could lead to confusion for new users of GDScript. I only wish that as & $ have similar appearances in regard to size and height and whatnot.
If we always use $ to indicate node referencing, then I would opt for !. This could be slightly confusing though as someone may be under the impression that it means "not", but the logicality behind it doesn't make much sense so I reckon it wouldn't be a huge deal. If not that, then maybe an alternative such as a '? Or perhaps ^? (Although I believe someone mentioned that ^ is used for instanced nodepaths in Godot 4, so maybe not a good idea)
Whatever the case, we have to consider that whatever we make the character be, people won't be able to start their nodes' names with it. This shouldn't be a problem as the official style guide limits node names to not allowing for such things, but I say should be as obviously, not everyone reads it or fully follows it.
&
is already used for StringNames.
!
is logical not. !"string"
is a valid boolean expressions and evaluates to false.
Personally, I lean more towards &.
Used for StringName
s. In fact, %
was almost the only remaining free character for the unary operator in GDScript. Other options required a combination with $
(e.g. $~
).
Also, $%NodeA/%NodeB
looks better than $$NodeA/$NodeB
precisely because %
is different from $
.
Please note that release 3.5 has taken place, and now changing the scene unique nodes character is a violation of backward compatibility, which should be strongly justified.
Also,
$%NodeA/%NodeB
looks better than$$NodeA/$NodeB
precisely because%
is different from$
.
Please note that this isn't what the proposal is about.
In a nutshell, the proposal suggests this:
%Node
-> $$Node
$%Node
-> $$Node
Whether or not % can stay in NodePaths as is is obviously relevant in the discussion, as they tackle the same thing, but both can technically coexist without nearly as much compatibility break as first though:
It doesn't seem mandatory, as both could coexist, despite the spoiling of choice it would cause. By removing it, long, mixed NodePaths such as
^"%UniqueNode/DirectChild/%UniqueNode/AnotherChild"
would no longer be possible.Please note that release 3.5 has taken place, and now changing the scene unique nodes character is a violation of backward compatibility, which should be strongly justified.
Indeed. However, as a community approving of this feature, it is fair to say that we want to make it right before it seriously is too late. Unfortunately 3.5 officially came out around the time Godot 4 hit feature freeze, and not enough people have tried Scene Unique Nodes to give enough opinions before this time.
$%NodeA/%NodeB
I honestly would prefer if this just, did not work.
Looking up nested scenes children wasn't something users were doing often in the past. Because it was finnicky and unreliable. And is something we shouldn't give them the tools to accomplish either.
The problem with $%"Unique Name With Spaces" is that right now it is $"%Unique Name With Spaces", which is a horrid construct of special characters because % is treated as if it was a wildcard substitute. But it's not, the list of unique nodes is cached anyways. So it's not doing a wildcard search, and doesn't belong inside of the string.
@Mickeon To clarify, you keep talking about $%
as a construct to replace with $$
. But right now it's $"%Name"
It just so happens you can omit the "" if your name doesn't require them. So that construct doesn't exist and can't be replaced. The % sign would need to fundamentally be removed from NodePath, and put into GDScript instead.
Please note that release 3.5 has taken place, and now changing the scene unique nodes character is a violation of backward compatibility, which should be strongly justified.
The feature was developed and merged with over a hundred posts in its proposal, with many questions left outstanding rather than answered in my opinion. It is a good feature, but the work wasn't put in to account for how it will interact with the rest of the engine and workflow. (It's hard to predict such things, so it's not like we could have known. And it was proposed for 4.0, not 3.x.) This was a case of overeager backporting. So, honestly, screw a little bit of backwards compatibility if we can make it so much nicer to work with.
Funfact: The original proposal used @
I agree that there are rational ideas in this proposal, and it would probably be better to go this way initially.
But it seems to me that the problems described in the OP are exaggerated in order to change something. It is unlikely that someone will use all these different ways, most will prefer the shortest option.
There are many ways to access nodes, even without considering unique ones:
get_node("Node/Path")
get_node(@"Node/Path")
get_node(NodePath("Node/Path"))
$Node/Path
$"Node/Path"
But this is not a problem.
As well as combining unique nodes with a format operator. This case is too rare to worry about.
But I want to emphasize: I'm NOT strictly against this proposal, I'm just confused if this issue is worth such attention. But given that this is a very young feature, I think the maintainers here should be able to predict the remaining lifetime of the 3.x branch and how quickly the main user base will switch to 4.x. Perhaps breaking compatibility here is worth it.
this proposal as well as #5048 both hint at the removal of the unique node accessor from node paths.
I'd move this more into the foreground of this discussion as I find that's when these proposals begin to really make sense.
the arguments I made were assuming %
(or any other accessor for unique nodes) no longer being a thing in node paths.
a good bit of the critique in this thread boils down to the suggested changes not being reconcilable with the accessor as part of node paths. that critique would no longer be relevant.
why remove %
from node paths?
it won't be used commonly and on top of that, is probably not something that should be encouraged in the first place.
Mickeon explained:
I do find that the most important Nodes are typically referenced at the beginning of the Scripts, as variables already. This is ideal, as they act as a general list of dependencies the Script relies upon to function properly. Thus, the simplest, likely more common way to dig down a hierarchy of important Nodes is by typing a much more familiar syntax, such as
var node = abc.def.ghi
.
and TheDuriel, too:
Looking up nested scenes children wasn't something users were doing often in the past. Because it was finnicky and unreliable. And is something we shouldn't give them the tools to accomplish either.
now with no longer having to consider the accessor as part of node paths, look at this again:
$
matches get_node()
$$
matches get_unique_node()
no alternatives. uniform and intuitive. something you may logically derive even without looking at docs. that's what these proposals are about, I think.
to be clear, as long as the %
accessor remains usable in node paths, I do NOT think that the %MyNode
shorthand should differ.
Agreed!
Now replace $$
with a single unique symbol, and I am fully on board with what I said.
$$
looks like a typo. Has a distinctly different function. And is hard to visually parse at a glance compared to single $.
@ is sadly somewhat unavailable despite its very fitting meaning, thanks to its new use in exports. But that still leaves others commonly found symbols available.
thanks to its new use in exports.
not just @export
, in godot 4 several keywords are replaced by what is referred to as annotations:
@onready
@tool
@icon
@warning_ignore
@rpc
in the future, additional annotations may be added that are not just used in class declaration but also in function scope (@warning_ignore
already is I believe), and since these expressions use @
as a prefix as well, I think it would clash too hard with @mynode
, if it even can be made parsable.
Now replace $$ with a single unique symbol
ok but again, easier said that done.
§
is only on international keyboard layouts as you pointed out.
when it comes to a single character, %
likely remains the most suitable.
other options may be: °
~
|
, which all seem really awkward.
Has a distinctly different function.
since get_unique_node()
can be seen as a flavour of get_node()
, the existing function of $
is one of the reasons I think $
with a second character makes a lot of sense. we still want to get a node, after all.
personally my favourite remains $*MyNode
, which would read as follows:
$
- get a node
*
- by unique
MyNode
- name
the awkward % icon in the editor GUI would then be replaced with an asterisk like star icon - fittingly as an unique node is a kind of favorite node, and the star is commonly associated with favorites.
when it comes to a single character, % likely remains the most suitable. Other options may be: ° ~ |, which all seem really awkward.
°
is absent on most keyboards, ~
is bitwiseNOT, |
is bitwiseOR. So... nope nope nope.
Edit one year later: The last one might actually work as it's a binary operator.
° is absent on most keyboards
ah, wasn't aware, that's out too then
~ is bitwiseNOT, | is bitwiseOR
yea but that's fine, the bitwise operators are pretty much available in this context (as a prefix to a string).
see ^
already being used for NodePath and &
for StringName without causing problems.
which is not to say that I advocate for those, just confirms my point that it's really not trivial if possible at all to find a single character other than %
to take this role. $
with a second modifier character such as $$
or $*
makes most sense and I see no issue with it. perhaps in the future we'll have a third flavor of get_node()
and then it's great to have the extensible syntax build on $
. $%Name
is just horrid though.
On my keyboard I do not have easy access to ~
. I have to write the ASCII code for it every time.
@Mickeon: Scene Unique Nodes are inherently cached,
This is just not true. Accessing scene unique nodes has a similar speed to accessing non-scene-unique nodes. There is a noticeable performance penalty when getting nodes, less than a microsecond for each, but it adds up. Here are some test projects to benchmark this, for both Godot 3.5-stable and Godot 4.0-alpha14: BenchmarkingDollarSign3.zip BenchmarkingDollarSign4.zip
heres a hot take that literally no one will agree with: fix everything all at once instead of trying to find weird two-character operators that arent taken to try to fix individual things. ie rotating everything so it makes more sense, but you have to change /everything/ to do this which is probably confusing but potentially worth it.
ANNOTATIONS
&annotation
for annotations, like hey all the normal rules AND this extra rule. not sure how annotations are different from plain old keywords tho. seems kinda arbitrary. this wont be confused with stringnames if you accept STRING-1|2|3.NODES
"../"
just do ../
.@Node
for nodes because its "at". annotations are freed up so this is fine.#@Cached
for cached nodes because hash? meh.#Cached
. this wont be confused with stringnames if you accept NODE-1 and STRING-1|2|3.:CachedNode
. not sure if godot could support this or not.#@~Unique
for unique nodes because ~
does something kinda similar in filesystems. note that theyre [cached] [nodes] [with no path]. these could be piecemeal perhaps. the syntax is pretty rough tho lmao.~
for this. ~Unique
.~
.#*Unique
because *
is wildcard.*Unique
if wont break everything.@("string_name")
for dynamic node names in get_node. if you don't change node to @
then still use $("string_name")
. likewise whatever would be appropriate given caching and unique decisions. ie `~("string_name").DIFFERENT KEYBOARDS
~
mean the same thing, to support more keyboards and symbols.STRINGS
#"string"
for stringnames, because #
is usually hash and this is like a reverse hash?:StringName
instead of :String
to ensure a string is interpreted as a stringname instead.:Path
as well.^"path"
i didnt use ^
for anything here.$s
for string formatting because it looks the most like a letter - the s in string.+
dynamically cast everything to string by default so then its "hi my name is "+name+"."
, like god intended. functions could format floats to strings with configurable precision etc. "pi is approximately: "+floatstr(PI,2)
.!, %, `, |, \, and possibly ^ would still be free for more stuff later on.
@BaddRadish I heavily suggest making a new Proposal or a Discussion for this, because your suggestions prove difficult to discuss in a comment section that should specifically be about this current Proposal.
@BaddRadish I heavily suggest making a new Proposal or a Discussion for this, because your suggestions prove difficult to discuss in a comment section that should specifically be about this current Proposal.
i disagree that this is not "about this current Proposal". i only talked about things other people talked about in the comments. the conversation is going in circles because people aren't addressing the syntax as a whole. "we cant do logical thing A because arbitrary thing B already exists". need to play musical chairs to fix the problem or /none/ of the above suggestions will work.
@BaddRadish What Micky was saying is that discussion of a particular proposal should focus on the pros and cons of the provided solution, as well as adding polish to the proposed implementation where needed. Whereas "I have a radical idea that would also fix the underlying issue" deserves its own proposal, which you can then link here.
The GDScript team talked about this proposal during a meeting. We think that the proposal was a good idea when it was written (August 2022, before the 4.0 release), but now, it's not something that we think is a good idea for now, as % sign is widely used right now.
Thanks @Mickeon for the proposal though!
Describe the project you are working on
A Typical 2D Godot game, dipping my toes with Scene Unique Nodes.
Describe the problem or limitation you are having in your project
Scene Unique Nodes are a positively received feature, but I do personally find the way this feature is accessed in via code to be potentially troubling in the long run, and it all has to do with the implementation of "%":
get_node("%Node")
feels... unrefined, to say the least. Even with mastery of the language, the use of a character to symbolise this interesting way of accessing Nodes feels inherently unpolished, seemingly going against the simplicity to read code, and ease of access that Godot strives for.Very much granted, this is a highly unrealistic example, but it showcases how overloaded the operator is.
get_node("%Node")
is valid syntax, and so are$%Node
,%Node
, and$"%Node"
. I personally believed GDScript was designed to write code that's easy to look at, but with many variations of the same operation, as well as the usage of a very arbitrary character to symbolise it, this goal is quite potentially hindered.get_node()
. This is unlike the "$" literal, that has been commonly utilised in every tutorial, has been simply summarised as a shorthand, but most importantly, servers one, and one specific purpose only, across the language.Describe the feature / enhancement and how it helps to overcome the problem or limitation
Implement a "$$" operator/literal. As "$" is considered to be a shorthand for
get_node()
, it would intuitively make sense for "$$" to be a shorthand for a theoreticalget_unique_node()
#5048. It fits like a glove. Still accessing Nodes, but in a way that's different and special enough to warrant its own operator that is easy to tell apart and is just logical, instinctive to type.This operator would obviously be summarised in the GDScript Basics, like "$" already is.
GDScript is no stranger anymore to double-character operators. In fact, recently, the exponentiation operator has been merged. Despite
pow()
already existing, its inclusion feels somewhat natural, simple. If "*" symbolises multiplication, it fittingly makes sense "**" is a stronger, albeit more specific variation of a multiplication.This proposal also heavily suggests to outright remove the "%" as a Scene Unique Node accessor. It doesn't seem mandatory, as both could coexist, despite the spoiling of choice it would cause. By removing it, long, mixed NodePaths such as
^"%UniqueNode/DirectChild/%UniqueNode/AnotherChild"
would no longer be possible, and other languages may especially struggle fetching highly-nested Nodes. Although, it's really worth debating the usefulness of this somewhere else.To conclude, this inclusion may warrant a change of Scene Unique icon on the SceneTree, if it's still being utilised.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
If this enhancement will not be used often, can it be worked around with a few lines of script?
It cannot.
Is there a reason why this should be core and not an add-on in the asset library?
The entire topic is core and cannot be modified with add-ons.