Open aaronfranke opened 4 years ago
A few notes/questions:
$Node@@2
. The $
should be used for accessing nodes that are already in the scene before ready.I don't think it is a good idea. Some users might rely on dynamically changing sub-nodes, so this behavior would lead to undefined references.
The thing is that I am not even sure this would lead to a significant performance improvement for several reasons: 1) internally the get_node functions uses NodePath (a list of StringName) instead of Strings, so everything is using reference and the string is not re-analyzed everytime, 2) accessing the node following the path is a very fast operation, which should not be long to execute at all. Most of the time it's a matter of iterating over the few components the path contains 3) adding a cache system requires checking when the cached values becomes invalid, as we don't want the engine to crash if a reference becomes wrong. This requires additional processing which might not be worth the time gained by caching the node.
Anyway, before trying to improve performance in a given part of the code, we have to make sure this is a bottleneck for some users, or that the gain will be really significant for everyone. So unless someone make a benchmark proving that this part of the code is slow, it's not worth making the engine code significantly more complex.
I store node references in vars and it does make a performance difference, at least in functions that are running often (e.g. physics_process and process() ). I don't have a benchmark on hand, though.
I store node references in vars and it does make a performance difference
If you access the node several times per frame it surely likely does. But I believe such case is an optimization to be done on the user's side, as he can know if the node is eventually going to be replaced or removed from the hierarchy during the processing. I believe the most common usage is to access a node once per frame, so in such situation I am not sure is it makes a big difference
I feel this is too much magic. If the script is long, you're now adding a lot of stuff on the user's _ready()
function behind their back. While it's hard to make it too slow, there is an added penalty. Maybe the user don't have a _ready()
function at all and will be wondering why the profiler says there is some time spent in _ready()
.
And the more obvious: what do you do when a node is freed? Because if you call get_node()
you'll get null
but if you are using a cache then you just have pointer to an invalid object.
Also, the user might remove a node and replace with another using the same name. If you keep a cache, then the $Node
shorthand will only point to the old node.
It's also possible that you make some cases worse. If the user is using _ready()
to connect signals:
func _ready():
$Button.button_up.connect(_on_Button_button_up)
Now it has a cached value that will never be used again.
Of course we can be clever about usage, but I do think this will lead to bugs into the user code that will be hard to pinpoint. The only way to get around those would be using get_node()
instead of $
. What would happen is that the shorthand would get bad rap and people would stop using it, making this optimization pointless.
If we are going to be clever about usage, then we can consider adding warnings on cases where this is used inside functions called often. Like this:
func _process(delta):
$Sprite2D.rotate(PI * delta) # Warning: Consider caching the reference to "$Sprite2D" to avoid getting the node from tree every frame.
Hm, good point. Could we get a caching equivalent of get_node() (cache_get_node() or some such), or would that require a new shorthand?
And the more obvious: what do you do when a node is freed?
As stated, if you are working with a non-static node tree you would just use get_node()
instead of $
. I really don't see the problem, since in most cases I've seen, $
is used to access nodes that don't change, and you can just use get_node()
otherwise.
As stated, if you are working with a non-static node tree you would just use get_node() instead of $.
You are talking about the user point of view, but the problem is not there at all.
What is difficult to do is implementing the caching system while guaranteeing that the reference is reset to null when the node is replaced, moved or removed from the tree. This would require adding checks in every move or delete operation to make sure that we cannot have a wrong pointer somewhere in the code (as the engine should not crash at all). This will, as a consequence, slow down those other operations.
If a node is able to be replaced, moved, or removed, users should use get_node()
instead of $
. It would be documented that $
doesn't work for nodes that can be replaced, moved, or removed. This way it is clear to users who would need to call get_node()
every time that a method is being called, and would make the more common case where $
is used faster.
@vnen: you're now adding a lot of stuff on the user's
_ready()
function behind their back.
Conversely: Users who might assume $
has this behavior have get_node()
called behind their back.
I think you don't get the point. This does not changes anything. Even if the user know that, the engine should not crash at all. And this is not negotiable.
Even if the user is not supposed to move or remove the node because "it's not supposed to work", we still have to add checks to simply avoid crashes. Checks that are going to slow down everything else.
If that is true, wouldn't crashes also exist with onready var x = $X
? I haven't noticed any. EDIT: This is both a reply to @groud and @vnen when @vnen stated this:
And the more obvious: what do you do when a node is freed? Because if you call
get_node()
you'll getnull
but if you are using a cache then you just have pointer to an invalid object.
Ah yes you are right, I did not think about that. I am not sure about how this is handled in the engine code, it's true that variable referencing nodes should not cause the engine crash too.
onready var x = $X
would translate to
onready var internal_x = get_node("X")
onready var x = internal_x
@nathanfranke I meant onready var x = $X
with the current behavior.
I don't really like this either, notably because caching is only valid if you know your branch will be static. It might not crash if you just move the node elsewhere in the hierarchy so the ref is still valid, but then the code no longer makes sense. Now from my experience, static nodes is very often the case, and I use onready
caching pretty much all the time (which is why at first it sounds like a good idea). However I'm not only doing it for performance: in my mindset, references to external stuff (assets, directories, other scripts, node paths) must appear on top of the script, and only once, instead of being spread and repeated throughout the script. It also makes it easier to re-arrange the tree or rename nodes.
onready var x = $X
would translate to
onready var internal_x = get_node("X") onready var x = internal_x
Besides, I wouldnt want this to happen, it would make my use case worse. I don't like this kind of magic. Other than that, I feel I'm not the target audience :p
Here's a thought:
var x = $X
- Maintain current behaviour so as to not break compatibility
var x = $$X
- Cache variable implicitly. Any time $$X
is accessed again, the cached reference will be used.
This doesn't break compatibility, adds a shorthand that both new and experienced developers can take advantage of, and still allows for developers to explicitly decide whether or not they want a node cached.
My proposal which also endorses better code readability with multiple scripts and nodes
vnen has a good point I think there are better alternatives.
I still stand by my original proposal. For any given edge case where the proposed behavior of $
is not desired, you can just use get_node()
instead. We can break compat in Godot 4.0.
@aaronfranke I want to use $
because it makes query faster (better loading and instancing times), but I don't want it to dupe all my onready
vars in which I already do caching.
My proposal which also endorses better code readability with multiple scripts and nodes
# shorthand for onready var label = $Label some_keyword label, "Label"
That's not much different of what you can do today:
@onready var label = $Label
Edit: just noticed the "shorthand" comment. It's not much of a shorthand if you need to type almost the same amount of tokens.
I don't see the problem with storing everything in variables. I always store nodes in variables in onready
. In my opinion it makes the code cleaner. You see all node-dependencies in one place.
And if the hierarchy changes you have one place where you can adjust everything.
When using the $
throughout your code, you have to find every instance where you used it and change it, which is quite error prone.
Edit: And you also have full control if you want to cache it or if you want to overwrite it at runtime.
@BeayemX You can just do onready var x = get_node(@"X")
in your case with this proposal. EDIT: Added @
@aaronfranke and that's precisely not good, because get_node()
has to build a nodepath to do this, while $
makes it at compilation time, in addition to be shorter to write. And it's been that way since it was introduced, I don't want to revert everything back to get_node
if $
still duplicates vars in this use case.
@Zylann Interesting, I didn't know that. You can just do const x = NodePath("X")
to make a compile-time NodePath, then get_node(x)
. The use case you are describing can be worked around with lines at the top of scripts just like the use case I'm describing with static nodes, but personally I have never had a need to have a compile-time NodePath with a dynamic node, so I think it's a less common case. For example, look at the demo projects.
EDIT: Just use onready var x = get_node(@"X")
, the @ sign means NodePath literal.
EDIT 2: In Godot 4, use ^
instead, like ^"X"
.
In the end we both want better performance. Storing references to non-changing nodes is arguably the most common case, which is why I want $
to do that. I don't want to have many lines at the top of my script, and lose the special green syntax highlighting, if I can avoid it. I want the most common operations to be high-performance and elegant.
You can just do const x = NodePath("X") to make a compile-time NodePath
And write even more code, sorry^^"
I have never had a need to have a compile-time NodePath with a dynamic node
The case I described isn't dynamic, and I use it in basically every script that accesses child nodes, in all my projects (I'm not the only one doing that). Also demo projects aren't representative of that.
In the end we both want better performance
In my case (and some others) this is not just about performance. I also want more maintainable code through decoupling, which happens to be done by explicit caching for fixed nodes, and const
paths for dynamic ones (very rare, tho, I give you that). Which means implicit caching gets in the way by either duping vars needlessly, or forcing to downgrade my code and write more lines than before. The idea of auto-caching $
comes from good intention, but I can't agree to have it because of that use case conflict. That's encouraging an existing pattern by stepping over another existing pattern, and I don't quite like the idea that a cached state is implicitely being written to a script wihout using var
.
Maybe if you can guarantee member vars assigned from $
won't get a dupe, that would solve it, thought it's exceptional behavior which you could only guarantee if these vars were marked read-only.
Maybe if you can guarantee member vars assigned from
$
won't get a dupe
That should be possible to implement if the only usage is in an onready var
, but I think that $
would still have to make a duplicate variable if there is also other uses of it elsewhere in the script. This should be implementable without any look-ahead if done right.
Also, if removing the existing behavior of $
is deemed unacceptable, I would be fine with @Mantissa-23 's suggestion of $$
, but do note that I would end up using $$
basically everywhere and it does seem a bit weird to make the more common operation longer in characters. It would be like JavaScript's ===
.
I agree it's a bit weird but maintaining backwards compatibility is important. Breaking backwards compatibility too frequently can bleed users pretty easily. Breaking compatibility like that might make sense for Godot 4.0, but I also think a lot of people are expecting 4.0 to be largely a "backend" upgrade that just makes their game better without really affecting the way they make it.
I vaguely recall var x = @"/some/node"
being usable in place of var x = NodePath("/some/node")
, so maybe onready var x = get_node(@"/some/node")
is the secret sauce?
I wonder if it would be a better practice to encourage users to use export nodes (once they are supported) rather than hard-coding node names.
export(Label) var my_label
func _process(delta):
my_label.text = "Bad FPS Estimation: " + str(1/delta)
@PetePete1984 Based on the documentation it does seem like get_node(@"X")
is the same as $X
Another advantage of this proposal is that it means $
will error immediately if the node doesn't exist. I ran into a situation today where I was refactoring and $
had a name mismatch, and I didn't notice the problem for a few minutes until that line of code was called.
@aaronfranke I still stand on my opinion. This proposal is 100% beneficial only for people coding without onready
declarations, the downside for others being a downgrade of what's needed to write or a memory dupe.
export(Label) var my_label
This being a killer feature, better than both options IMO.
I never wanted my scripts to use hardcoded node names, that's why I don't use $
all over the place, like this proposal assumes.
Besides, you can't type-hint $Stuff
, annotate it, and can't have autocomplete on it unless you open Godot and a scene that uses the script.
It's also possible that you make some cases worse. If the user is using _ready() to connect signals:
@vnen Is it possible to simply not make this "caching" happening in the _ready
and _init
(for optimisations purpose)? They are the only functions where this proposal is not relevant.
I feel this is too much magic.
$Node
is an alias of get_node("Node")
, the proposal make $Node
an alias of onready var _node = $Node
. Nearly the same magic, no new behaviour, just a different alias that make possible to hide big bunch of useless variables.
the user might remove a node and replace with another using the same name. If you keep a cache, then the $Node shorthand will only point to the old node.
Seems to be an edge case that I never saw. I think is a bad practice to use $ on nodes that can be replaced. If really needed, the hidden array that store cached variables can be made accessible by functions to be able to make update/delete operations.
@aaronfranke I still stand on my opinion. This proposal is 100% beneficial only for people coding without
onready
declarations
I disagree this proposal will benefit to all users, and a lot. They are main benefits:
Clearer for users: $ for static node, get_node() for dynamic ones
Save time by avoiding a repetitive task (and boring)
Optimise the code for those (often beginners) who use $ alias in process loop.
Prevent error in the editor, if the node is static, it should exist in the tree.
And the best: to make the script a way shorter/readable by removing endless useless declaration. They are useless because they are only added for caching/performance purpose, they are not "real" variables for the script. On my "serious" amateur projects, I have often scripts where when you open them in the editor, you only see a whole page of onready var
declarations if you don't scroll. That makes scripts hard to read and understand, and this situation is probably worse on professional projects.
The only drawback I see, is to understand that this alias should be used for static node (which already represents the vast majority of usage), the price seems cheap.
this proposal will benefit to all users, and a lot. removing endless useless declaration. They are useless because they are only added for caching/performance purpose, they are not "real" variables for the script.
Did you read my previous posts?
They are far from useless. And caching isn't the only reason. This kind of practice tells coders right off the bat which nodes the script depends on. It makes it easier to maintain when node names or their path change as the project evolves. They often shorten the name when /
are in the path. And they can be type-hinted, annotated, and commented, without requiring Godot and the scene to be open (think about external editors, plugin dev and Github reviews). Eventually exporting node types directly could even get rid of the need to hardcode and get them. We should not assume everyone should use $
or get_node
all over the place. There are very good reasons not to.
https://github.com/Zylann/godot_heightmap_plugin/blob/e8cc8e047d5f862f5fdc7914e2bddc6c63a146e3/addons/zylann.hterrain/tools/brush/settings_dialog/brush_settings_dialog.gd#L11-L22
The idea of making $ a bit more efficient under the hood for users that write it a lot in functions, is a nice idea, I am not blanket against it, even though I'm not the target audience for all reasons cited earlier. It's just that the suggested implementation so far is causing unnecessary waste in code designs I described earlier. So what I thought about, is maybe caching storage should only occur if $
is used in functions, since that's where the optimization makes sense (and then would not be allocated if only used in member initializers).
Did you read my previous posts?
Yes I'm not convinced by them actually, but I learned the @"" syntax in the process :)
This kind of practice tells coders right off the bat which nodes the script depends on. It makes it easier to maintain when node names or their path change as the project evolves.
When I see the screenshots of @aaronfranke in the first post. I think the opposite, the version without extra var is more readable (+ extra highlight), I wouldn't argue further why these variables are unnecessary because at the end, we can agree that's a matter of preference.
But "your" preference/practice require to write many extra lines of codes that some (I suspect a lot) thinks they are useless. Moreover, this proposal doesn't prevent your practice from being applied if you want:
var node = $node
And they can be type-hinted, annotated, and commented, without requiring Godot and the scene to be open:
Same, nothing prevent you to store and add a type if you want:
var node: MyNode = $node
I don't understand your opposition if nothing in this proposal prevent you to apply your preference/best practice.
For the other arguments:
$
with dynamic nodes, I never saw that, but this proposal require you will need the change to get_node(@"X")
.You guys know how I think about these things, I think that most of the time, explicitness is better, and things that hide what happens under the hood to the programmer in a non-obvious way, even if it means writing a bit less code, is generally bad.
Originally, when I created the $ syntax, I saw that this may be a problem in cases where you wanted performance. Still, by experience, in far most cases where you use this, you don't really need that performance. It's only in a few situations that you may need the node to access it faster.
For this, I thought I could add a "cached $Node" syntax to tell the programmer that this node is cached, but the problem with this is that when this node is cached still remains a mystery. This is why the onready keyword was born. Its a much simpler and flexible solution, and very explicit in nature, so its obvious what it does when you look at it. I don't think this needs to be changed.
Still, an alternative that I always thought could work may be creating something like:
onready $Node
onready $Node2 as AnimationPlayer # for type safe
This way, if you already have code written and you want to optimize it, its just easier and its still fairly explicit. That said, if the node is far away, someting like
$Sprite3/Something/AnimationPlayer
You may still want to use onready var and assign it to just avoid typing all that, so this would more or less work with node paths that are not too long. Even with that would it still be useful?
Here is a small project I made to benchmark the time required to get nodes with $
: BenchmarkingDollarSign.zip
On my system, I get output similar to this (the middle two lines add up over time so it depends when you look at the output, and the last part is a fairly trivial operation to give a sense of scale):
Average time to get nodes on process: 116.389646 microseconds per frame
Total time wasted getting all nodes on process: 0.640725 seconds
Total time wasted getting each node on process: 0.006407 seconds
Average time to set pause mode: 51.663034 microseconds per frame
It seems that each call to $
takes about 1 microsecond on average. This means that if you have a project with ~166 calls to $
per frame, and your project runs at 60 FPS, then an entire 1% of your execution time is dedicated to the node getting logic. On the other hand, if you have a project with 166 onready var x = $X
lines, you would have 1% extra time per frame compared to if you used $
everywhere, but then you would have an extra 166 lines of code to maintain.
I just tried that benchmark on a Ryzen 5, here $
results in process for average times are:
Average time to get nodes on process: 46.86875 microseconds per frame
Average time to set pause mode: 22.041667 microseconds per frame
But I noticed something interesting: if I execute those 10000 times per process instead of once in the goal of getting more "isolated" result with less printing, I get:
Average time to get nodes on process: 237282.727273 microseconds per frame
Average time to set pause mode: 106606 microseconds per frame
Which, when divided by 10000 to be comparable to the first result, gives 23.7 microseconds per frame for $
, and 10.7 microseconds to assign pause mode. It doubled in speed. Not sure what to think of that, although the ratio is similar.
Curiosity aside, when you say 166
extra lines to maintain, I disagree. Sure you have 166 extra lines [not all in the same script] if you completely disregard what they are, but these lines can lower the base maintainance cost and have more possibilities (cf previous comments). That might just be less true if you access a node only once in your script, of course. It's coding preferences anyways.
I still think, as said in https://github.com/godotengine/godot-proposals/issues/996#issuecomment-671316234, that we can have caching, if it does not redundantly occur from code written in onready initializers.
Note: we'll also need to make error reporting account for automatic caching. If a node can't be found (which would occur in the ready phase), we can't just have the error point at _ready
, or anything, since there won't be code there, and none of the references to the node will have been reached yet.
(a related issue is the fact the debugger doesn't break when errors happen in C++ functions called by scripts in general and I wish that was adressed at the root)
A change I would like to propose to this system is cache the $ calls BEFORE ready time, so when you instance the node, the $ calls are already cached and available in init or other before ready method. With this approach the user can preinstance the levels in a menu thread for example and move heavy processing algorithms that requires child node references before ready. I already did something similar as optimization using NOTIFICATION_INSTANCED.
I think https://github.com/godotengine/godot-proposals/issues/3695 is a better way to address the problem at hand for two reasons:
$
or $$
will never be able to do this on its own, simply because the node type can't be known at compile-time (or even checked for actual presence). However, the editor side does know this when performing autocompletion.Most programming IDEs that deal with importing packages (Python, Java, PHP, …) also have implemented a similar kind of autocompletion.
This is part of GDScript compile time optimization, it doesn't makes sense for me add a different operation just to cache the node because this feature proposal is chiefly improving the behavior of an existing operator($).
However, there may be corner cases where the compiler can't resolve to cache a node (and it can be cached but the code flow is difficult to compute) and the user want to explicit cache it, so in that case it makes sense to add the explicit $$ cache operator.
Therefore, for me the best solution is: Enhance the gdscript compiler to cache $ node references where it can resolve and add the explicit $$ operator that [tries to] caches the node regardless whether the compiler optimized it and throw an error if it wasn't possible.
alright I just read through the entire thread and while the critique made sense to me, I did not see a single comment arguing that $$
was a bad idea.
absolutely; let the programmer decide where it makes sense to use the current behavior and where caching would be useful. but that doesn't mean we can't have this.
it seems like there are two camps of us:
those who like to declare a lot of node references as onready, and those of us who prefer skipping this and making use of $
instead. personally I belong to the second camp as I dislike having to add a property to class scope for every node I am going to use in code (or even two variables when the reference is derived from an exported NodePath). I am also quite fond of the green syntax highlighting communicating clearly that we are dealing with a node. but this shouldn't be about which one of these two patterns is superior, of course. let's support both, please.
by the way, here is an up to date performance test I just ran in the current alpha:
# 1 mil iterations each
236ms # direct node reference access
278ms # $NodePath
278ms # get_node(^"LiteralNodePath")
533ms # get_node("NodePathAsString")
as you can see, performance of get_node() with a string passed is poor, however with a constant NodePath (which is always the case when using $
), it's pretty good. an argument can be made that this proposal isn't needed as performance is already good enough. I think it would still be a nice to have though.
as for the type hint, couldn't it added implicitly the moment the caching happens?
edit: also while I support #1048, I don't agree that it renders this proposal obsolete. again, I don't want to declare variables at class scope unless I have to and I have no desire to expose anything other than things that make sense as configuration to the inspector.
as for the type hint, couldn't it added implicitly the moment the caching happens?
The caching happens at runtime, since the script can be used in multiple places and may have different children.
alright I just read through the entire thread and while the critique made sense to me, I did not see a single comment arguing that
$$
was a bad idea.
There is the following comment and I agree with it:
You guys know how I think about these things, I think that most of the time, explicitness is better, and things that hide what happens under the hood to the programmer in a non-obvious way, even if it means writing a bit less code, is generally bad.
If something is used frequently, then you need to store it somewhere (in a variable). This is a universal solution that works not only with Node
references. $$
is too narrow a solution, which is also confusing.
$$
is purely for optimizations. A similar situation was with iniline
(#3760). It seems to me that at the language level there should not be things intended only for optimizations.
278ms # get_node(^"LiteralNodePath") 533ms # get_node("NodePathAsString")
In theory, we can make the GDScript compiler smarter and optimize the get_node(<constant string expression>)
case. This optimization will not add confusion.
while I think it's sufficiently explicit when people learn about $$
as "the $ with caching", I see that it's probably not worth it since in your own words:
This proposal is 100% beneficial only for people coding without onready declarations,
the thing is we have no straight forward way of implementing our own explicit caching without using onready
.
$$
would essentially be an onready
for people preferring $
.
in that sense, it can be seen as more of an syntactic sugar thing than a performance thing.
In theory, we can make the GDScript compiler smarter and optimize the get_node(
) case. This optimization will not add confusion.
had the same thought. it's a waste that get_node()
calls that have no variable component passed into them perform so poorly because the user did not think about explicitly using the literal node path syntax. after learning about this thanks to your comment yesterday in the node$path
proposal, I had some searching & replacing to do.
I'm completely new to Godot, so I'm trying to decide if I want to be in the onready/get_node or $ camp. My first concern was the performance implication of the $ operator, so I found this thread. I understand not wanting to break backwards compatibility, but what if there were a source file or (preferably) project-level directive to enable caching (like "option explicit" in JavaScript)? It would let those who know what they're doing take advantage of caching right away without breaking old projects. Then, after a few releases the default value of the option could be flipped to cache by default.
I understand not wanting to break backwards compatibility, but what if there were a source file or (preferably) project-level directive to enable caching (like "option explicit" in JavaScript)?
We prefer not making GDScript behavior dependent on directives by this (or worse, project settings). Scripts may be copy-pasted from a project to another, and this should never cause them to break because a project uses different settings from another.
That said, breaking compatibility for 4.0 is not a concern as compatibility was already broken there a long time ago.
EDIT: If https://github.com/godotengine/godot-proposals/issues/1048 is implemented, it may make this proposal obsolete.
I disagree that #1048 makes this proposal obsolete.
I thought this was at least in part about reducing the ugly boiler plate code of onready
declarations. having to declare a bunch of @export var
instead doesn't help with that.
However, now this code is ugly, since it is much longer than it could be and we've lost the green syntax highlighting in new_game() that tells us we're working with node children.
or did you perhaps mean to link the unique node proposal instead? that would make a bit more sense, but still, performance of %Node
and $Node
are currently identical, so no, still not really addressed.
EDIT:
If #1048 is implemented, it may make this proposal obsolete.EDIT 2: As @h0lley noted, #1048 doesn't actually make this proposal obsolete. It provides a clean alternative but it still results in boilerplate and a lack of green syntax highlighting unlike this proposal.
Describe the project you are working on: https://github.com/godotengine/godot-demo-projects/ but this applies to any project made in GDScript.
Describe the problem or limitation you are having in your project:
Here is a code snippet from Dodge the Creeps:
The problem is that this code looks elegant, but it isn't very performant. Every time this method is called, it has to call
get_node()
6 times, and 2 of those times are getting the same node (HUD). We can reduce this from 6 to 5 by caching the HUD reference like this:However, this method is still very inefficient, since it calls
get_node()
5 times every time the method is ran. The best simple option that I can see is to useonready var
like this:This means that
get_node()
is only called 5 times once when this node is created, and this method does not have to callget_node()
at all when the method is ran. However, now this code is ugly, since it is much longer than it could be and we've lost the green syntax highlighting innew_game()
that tells us we're working with node children.Describe the feature / enhancement and how it helps to overcome the problem or limitation:
The proposal is to make the
$
operator cache references on ready, so that user code looks like the top image, but behaves like the bottom image.For projects currently caching references via
onready var
, this would make code more elegant.For projects currently using
$
everywhere, this would increase performance.In cases where
get_node()
needs to be called every time, such as if nodes are created and deleted, users can just useget_node()
instead of$
. As such,$
would have different behavior fromget_node()
.Another option, described below, would be to add a
$$
operator with this behavior.Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
See above for examples of user code, but @vnen would be the one handling the implementation.
If this enhancement will not be used often, can it be worked around with a few lines of script?:
It can be worked around, but this is something that will be used often.
Is there a reason why this should be core and not an add-on in the asset library?:
Yes, because it will be used often.