Here's the info on Unique Names, since quite a few people mentioned this :) Unique Names are similar to exported references (in the sense that the scene tree position can change while retaining the path), although they still need to be accessed with the % symbol (the same as using the $ sign or get_node()). Pros of using Unique Names: - Similarly to exported references, you won't need to update your code when moving the node around in your tree - More readable paths inside your script Some Cons of using Unique Names: - For the best performance, it's still essential to store unique nodes inside of @onready variables, since they do still need to be obtained, and aren't properties - Uniquely Named nodes can ONLY be retrieved by a node inside the same scene. This means that instanced scenes with exported node references are much more flexible, especially when working with component systems. - If you have multiple nodes with similar names (especially in larger scenes), Unique Names can get tedious, and end up being more confusing than other methods. Performance of Uniquely Named nodes: - The performance is slightly faster than using static paths, but is still slower than using exported references Hope this helps! :)
I will realy enjoy video of how this performance is mesured. Can be a short. I'm really curious. I'm not a Godot Pro or anything like that. As far as I know the % does all the chaching for you. The main benefit for me is that I often need to re-position nodes in UIs and % names saves you all the renaming.
Just a minor note, you don't really lose runtime flexibility with exported variables, and they don't have to be assigned in the editor. You can set default values in the script declaration, so if you forget to set them in the inspector it'll still work, and you can still set them again during runtime, nothing prevents it. The only downside is they will revert to the exported value when the game is rerun, so you'd need to save the value, and load it after to get the prior runtime value. Most games with save/load do that anyway, so it's not a real issue. Also, export is basically the same as onready + inspector access. There's not really a performance difference. Export does process before onready though, as it's more in line with _init(), while onready is with _ready().
Also, the majority of nodes you access in Godot is part of the same scene as the script. It's how you structure logic, actions, reusability and everything in godot. So most child nodes that you export a reference to have the exact same lifecycle as the parent node/scene where you attach the script. When outside of that use case, groups are often very useful. For example if you have a PlayerHUD scene that reads back health etc from the player I usually just pick the first element of the player group and set the reference in onReady, and throw an error if a player cannot be found. I use c# so I haven't used unique names a lot, maybe that's preferred approach, but groups worked very well for "outside" dependencies in c#.
honestly one of the best things i ever learned in programming was to look for/keep in mind the hidden 'costs' of every line of code, in terms of processing, lookups, memory, etc. when i was a kid, computers were grossly underpowered, slow as shit, and memory was precious; if your code didn't take all this into account, it either ran poorly, or not at all. modern systems tend to obscure this (both a good and bad thing), but the fundamentals haven't changed. i'd love to see more developer content taking quick asides to explain "ok... so look at what's happening here *every single processing cycle*..." etc.
Great video idea with these short clarifications! If you haven't done it already, an explanation between some of the other virtual methods would be awesome. Like _process vs _physics_process or _input vs _unhandled_input. Stuff like that.
There's probably no difference between the performance of export and onready. If you look a the scene file and find the export property you'll see that a NodePath() is saved and not a reference. So, both export and onready use nodepaths. However, the export value is evaluated early and you can access it in the _enter_tree() function while the onready value is set later and available by the time _ready() is called. (Note: all nodes in a scene are created from the get go and then added to the tree from root to leaf. To verify add an _init() override that prints _init() was called for a node. Do likewise for _enter_tree() and _ready() to get a sense of the sequence.) Personally I use onready variables with unique names and just rename nodes to match what they are being used for making it less likely I'll rename them. Then moving them around wont break anything. I also don't like having extra exports hanging around when I child the scene into a level or something. This is especially true for very complex scenes where a lot of extra exports would be added and visible. But to each their own! If you like exports, by all means enjoy! While I personally don't do it, there's nothing really wrong with it.
Thank you for creating this content. I like these kinds of videos: short, to the point, and covering the basics. As it was mentioned in other comments, I'd have liked to see an example using %unique_names, perhaps that could go in another video. Best regards.
I use a mix of exports and onready, The exports i use for scripts that are shared between scenes. For instance, my playable characters all use a stats script that houses their health, stamina etc. I this allows reuseability, for logic thats shared between characters but not necessarily needing the exact same things like running speed etc.
It's just a guess, but onready nodes should be worse because godot still calls get_node to get the reference while with export, it seems, you directly assign the reference via the editor
Yes there is! Using Unique Names is still slower than setting exported references, although it is faster than using static paths. For the best performance with Unique Names, you should still store the reference inside of an @onready var. The only drawbacks of using Unique Names, is that node references will only be able to be accessed by other nodes inside that same scene, meaning that if you instance a scene with an exported node reference, it'd be a lot more flexible.
Great video 👏 with the export method, would there be a significant performance difference if you dragged a file from a folder rather than creating a node in the scene tree for that variable? Example: an mp3 file. Do I have to create an audiostream node inside my scene tree or is it fine to just drag the file from my sounds folder?
There is one big issue with using @export assignment on Label-nodes : more than once it happend to me that Godot removed all label references from the scene, giving me null pointer during runtime. Therefore I had to re-assign every single one of them. Imagine the fun if you have a lot of labels in your bigger szenes 😅
It depends a lot on the type of project you're making and what it is you're doing with that node, what it's interacting with. Exporting some nodes can lead to issues with serialization and there are some other potential issues. For smaller projects this may be fine. But it is NOT something you should be doing with 100% of your nodes, at any and all times. And most certainly not something you should do with larger games.
How would you go about improving that line? The only thing I can think of would be adding a condition to only update if the mouse pos is different than last frame?
@@queblegamedevelopment4143 Glad you asked. Based on what I understand, converting the mouse's position to a string with str(...) is the most time-consuming function in question, so reducing the number of times we call it is ideal. What you suggested is correct. We'd use the Node's _input(event) function to check if the mouse position has changed, and if it did, we'd assign str(get_global_mouse_position()) to the label's text. As far as I know, _input(event) is only called when user input is detected, so first checking for a UI change and then checking if it was the mouse position significantly cuts down on how many times str(...) is called. That said, for most games, this kind of optimization is likely unnecessary. Yes, it can add up, but performance drops are typically caused by rendering a lot of textures or poorly managing large amounts of data or memory, as opposed to calling a str(...) conversion each frame.
@@queblegamedevelopment4143 Hey, just rewatched to double-check, but I don't see any figures for the performance differences you're mentioning. Knowing the cost and benefit of multiple approaches helps people make the choice. Saying "faster" and "slower" is not engineering.
There were no figures shown in the video, as I assumed the efficiency boost was obvious. Using "faster" / "slower" is a great way to give viewers (especially less experienced users) the information they need in a quicker format :) Aside from that, the benchmark project I used to test these theories showed the following results: Figure A: Using $ to access nodes when you need them: - 20 nodes, having 2 properties accessed each, 10,000 times per frame | average of 120ms frames - 10 nodes, having 2 properties accessed each, 20,000 times per frame | average of 121ms frames - 20 nodes, having 2 properties accessed each, 100,000 times per frame | average of 1204ms frames Figure B: Storing nodes in onready variables for access: - 20 nodes, having 2 properties accessed each, 10,000 times per frame | average of 80ms frames - 10 nodes, having 2 properties accessed each, 20,000 times per frame | average of 83ms frames - 20 nodes, having 2 properties accessed each, 100,000 times per frame | average of 819ms frames (As you can see, the method I promote in this video is clearly more efficient than the alternative)
Here's the info on Unique Names, since quite a few people mentioned this :)
Unique Names are similar to exported references (in the sense that the scene tree position can change while retaining the path), although they still need to be accessed with the % symbol (the same as using the $ sign or get_node()).
Pros of using Unique Names:
- Similarly to exported references, you won't need to update your code when moving the node around in your tree
- More readable paths inside your script
Some Cons of using Unique Names:
- For the best performance, it's still essential to store unique nodes inside of @onready variables, since they do still need to be obtained, and aren't properties
- Uniquely Named nodes can ONLY be retrieved by a node inside the same scene. This means that instanced scenes with exported node references are much more flexible, especially when working with component systems.
- If you have multiple nodes with similar names (especially in larger scenes), Unique Names can get tedious, and end up being more confusing than other methods.
Performance of Uniquely Named nodes:
- The performance is slightly faster than using static paths, but is still slower than using exported references
Hope this helps! :)
I will realy enjoy video of how this performance is mesured. Can be a short. I'm really curious. I'm not a Godot Pro or anything like that. As far as I know the % does all the chaching for you. The main benefit for me is that I often need to re-position nodes in UIs and % names saves you all the renaming.
Just a minor note, you don't really lose runtime flexibility with exported variables, and they don't have to be assigned in the editor. You can set default values in the script declaration, so if you forget to set them in the inspector it'll still work, and you can still set them again during runtime, nothing prevents it. The only downside is they will revert to the exported value when the game is rerun, so you'd need to save the value, and load it after to get the prior runtime value. Most games with save/load do that anyway, so it's not a real issue.
Also, export is basically the same as onready + inspector access. There's not really a performance difference. Export does process before onready though, as it's more in line with _init(), while onready is with _ready().
Also, the majority of nodes you access in Godot is part of the same scene as the script. It's how you structure logic, actions, reusability and everything in godot.
So most child nodes that you export a reference to have the exact same lifecycle as the parent node/scene where you attach the script.
When outside of that use case, groups are often very useful. For example if you have a PlayerHUD scene that reads back health etc from the player I usually just pick the first element of the player group and set the reference in onReady, and throw an error if a player cannot be found. I use c# so I haven't used unique names a lot, maybe that's preferred approach, but groups worked very well for "outside" dependencies in c#.
honestly one of the best things i ever learned in programming was to look for/keep in mind the hidden 'costs' of every line of code, in terms of processing, lookups, memory, etc. when i was a kid, computers were grossly underpowered, slow as shit, and memory was precious; if your code didn't take all this into account, it either ran poorly, or not at all. modern systems tend to obscure this (both a good and bad thing), but the fundamentals haven't changed.
i'd love to see more developer content taking quick asides to explain "ok... so look at what's happening here *every single processing cycle*..." etc.
I didn't know get_node had such an performance impact. Thanks
Great video idea with these short clarifications! If you haven't done it already, an explanation between some of the other virtual methods would be awesome.
Like _process vs _physics_process or _input vs _unhandled_input. Stuff like that.
I was just wondering this a couple days ago. Thanks for sharing.
There's probably no difference between the performance of export and onready. If you look a the scene file and find the export property you'll see that a NodePath() is saved and not a reference. So, both export and onready use nodepaths. However, the export value is evaluated early and you can access it in the _enter_tree() function while the onready value is set later and available by the time _ready() is called. (Note: all nodes in a scene are created from the get go and then added to the tree from root to leaf. To verify add an _init() override that prints _init() was called for a node. Do likewise for _enter_tree() and _ready() to get a sense of the sequence.)
Personally I use onready variables with unique names and just rename nodes to match what they are being used for making it less likely I'll rename them. Then moving them around wont break anything. I also don't like having extra exports hanging around when I child the scene into a level or something. This is especially true for very complex scenes where a lot of extra exports would be added and visible.
But to each their own! If you like exports, by all means enjoy! While I personally don't do it, there's nothing really wrong with it.
As usual, the more static or predefined some info is, the faster it will be processed
Thanks for sharing this information.
Thank you for creating this content.
I like these kinds of videos: short, to the point, and covering the basics.
As it was mentioned in other comments, I'd have liked to see an example using %unique_names, perhaps that could go in another video.
Best regards.
I use a mix of exports and onready,
The exports i use for scripts that are shared between scenes. For instance, my playable characters all use a stats script that houses their health, stamina etc.
I this allows reuseability, for logic thats shared between characters but not necessarily needing the exact same things like running speed etc.
It's just a guess, but onready nodes should be worse because godot still calls get_node to get the reference while with export, it seems, you directly assign the reference via the editor
I never use $ or node paths in code. Always export variables.
Is there a difference between exporting nodes and setting them to "% Access as Unique Name"?
Yes there is!
Using Unique Names is still slower than setting exported references, although it is faster than using static paths.
For the best performance with Unique Names, you should still store the reference inside of an @onready var.
The only drawbacks of using Unique Names, is that node references will only be able to be accessed by other nodes inside that same scene, meaning that if you instance a scene with an exported node reference, it'd be a lot more flexible.
Great video 👏 with the export method, would there be a significant performance difference if you dragged a file from a folder rather than creating a node in the scene tree for that variable?
Example: an mp3 file.
Do I have to create an audiostream node inside my scene tree or is it fine to just drag the file from my sounds folder?
Great job
The missing one is %cached_name
Those are cached so are fast
I've added a pinned comment that explains the pros and cons of unique names in a bit more depth :)
Just use @ready with %unique names. Why would you want to drag n drop your whole node tree in export vars :D
that's exactily what I was going to say.
I was looking for this by the end of the video.
Использовать уникальные имена желательно тольок для обьектов которые уникальны или в единственном экземпляре иначе будут ошибки
I've added a pinned comment that explains the pros and cons of unique names in a bit more depth :)
There is one big issue with using @export assignment on Label-nodes : more than once it happend to me that Godot removed all label references from the scene, giving me null pointer during runtime. Therefore I had to re-assign every single one of them. Imagine the fun if you have a lot of labels in your bigger szenes 😅
DANG!! now I must rewrite 500 lines
😅😅😅
It depends a lot on the type of project you're making and what it is you're doing with that node, what it's interacting with. Exporting some nodes can lead to issues with serialization and there are some other potential issues. For smaller projects this may be fine. But it is NOT something you should be doing with 100% of your nodes, at any and all times. And most certainly not something you should do with larger games.
It is still inefficient. The str() function should not be in _process
True. The code example in _process function should have been something else, since the point of the video was about referencing Nodes.
How would you go about improving that line?
The only thing I can think of would be adding a condition to only update if the mouse pos is different than last frame?
@@queblegamedevelopment4143
Glad you asked. Based on what I understand, converting the mouse's position to a string with str(...) is the most time-consuming function in question, so reducing the number of times we call it is ideal.
What you suggested is correct. We'd use the Node's _input(event) function to check if the mouse position has changed, and if it did, we'd assign str(get_global_mouse_position()) to the label's text. As far as I know, _input(event) is only called when user input is detected, so first checking for a UI change and then checking if it was the mouse position significantly cuts down on how many times str(...) is called.
That said, for most games, this kind of optimization is likely unnecessary. Yes, it can add up, but performance drops are typically caused by rendering a lot of textures or poorly managing large amounts of data or memory, as opposed to calling a str(...) conversion each frame.
No one should give performance advise without measurements.
Measurements were taken :)
@@queblegamedevelopment4143 Hey, just rewatched to double-check, but I don't see any figures for the performance differences you're mentioning. Knowing the cost and benefit of multiple approaches helps people make the choice. Saying "faster" and "slower" is not engineering.
There were no figures shown in the video, as I assumed the efficiency boost was obvious.
Using "faster" / "slower" is a great way to give viewers (especially less experienced users) the information they need in a quicker format :)
Aside from that, the benchmark project I used to test these theories showed the following results:
Figure A: Using $ to access nodes when you need them:
- 20 nodes, having 2 properties accessed each, 10,000 times per frame | average of 120ms frames
- 10 nodes, having 2 properties accessed each, 20,000 times per frame | average of 121ms frames
- 20 nodes, having 2 properties accessed each, 100,000 times per frame | average of 1204ms frames
Figure B: Storing nodes in onready variables for access:
- 20 nodes, having 2 properties accessed each, 10,000 times per frame | average of 80ms frames
- 10 nodes, having 2 properties accessed each, 20,000 times per frame | average of 83ms frames
- 20 nodes, having 2 properties accessed each, 100,000 times per frame | average of 819ms frames
(As you can see, the method I promote in this video is clearly more efficient than the alternative)