Godot: Simplify getting the root node and finding nodes in general

Created on 31 Oct 2017  Â·  62Comments  Â·  Source: godotengine/godot

Godot 3.0

There are a few ways to get the root node in Godot, but none of them are straightforward, intuitive, portable, or concise. get_tree().get_root().get_node("rootNodeName") is an example of what I mean. It gets even worse when trying to get a node anywhere in the tree with a particular name: get_tree().get_root().get_node("rootNodeName").find_node("nameQuery"). If enough devs are storing the root node in a singleton property to avoid this boilerplate, then I feel that it should be more built into the engine.

We already have a get_nodes_in_group function in SceneTree, so getting nodes in the more global context is not unprecedented.

But I confess I would go as far as to suggest adding another single character shortcut like we do with the $ syntactic sugar for get_node. Another game engine uses the underscore character to be syntactic sugar for get node anywhere in the scene.
So, instead of writing get_tree().get_root().get_node("myRootNode").find_node("desiredNode"), you could just do _."desiredNode" or _.desiredNode.

I teach an introductory programming university class using Godot, and referencing nodes, especially as the tree mutates, is one of the most fundamental barriers concerning students picking up Godot.

archived discussion enhancement gdscript

Most helpful comment

I have not dug into the code fully yet so I do not know the structure, admittedly. Is there no way to reference nodes with something like? 'get_root().myRootNode.desiredNode' or 'get_root()->myRootNoe->desiredNode'

$"/root/myRootNode/desiredNode" or get_node("/root/myRootNode/desiredNode")

All 62 comments

get_tree().get_root().get_node("myRootNode").find_node("desiredNode")

If one finds oneself writing code like this, I think there is a design problem. This makes the code dependent on a very specific node structure.

I would rather put the code in a node local to the nodes you want to access, and retrieve the node with a relative path from there

Edit: Sent too early

maybe it's a habit of unity or some other engine?
there are several similar questions on Q&A also.
personally, I never try to find a node with starting get_tree() hm... :confused:

I just started getting into Godot today and this is the main issue I have had with the scripting so far. It is quite gross the way we find nodes currently. I hate string references (but see the need for them here.. so its all good), but there could really be some clean up with finding nodes. I would like a function call that returns the root at the least simplifying his first example to "get_scene_node(path)".

(If this is already a feature then I apologize)

I have not dug into the code fully yet so I do not know the structure, admittedly. Is there no way to reference nodes with something like? 'get_root().myRootNode.desiredNode' or 'get_root()->myRootNoe->desiredNode' Obviously there are reasons why this can't happen as I am sure it has been thought of several times, but it would be kinda cool...

Maybe I do not know enough about the syntax to even be talking, but it is a first impression that I am sure many developers moving over are going to see.

I agree with eska014. There are basically two reasons why you would want to have such a find_node()-function, and those are a) you are writing procedural code or b) you are writing components, and both is not encouraged in Godot (and is breaking object orientation or at least writing suboptimal OOP code).

A procedure is a sequence of commands that alters its surrounding. If your class acquires the tree and modifies it it's technically not object oriented, it's procedural, since the functions in a class are supposed to alter their own state only (or change objects owned by the class via the public interface), that's what makes them methods rather than procedures.

A component on the other hand is a class that imposes requirements on its environment. If you are acquiring nodes outside of the current scene in order to read data from them and react to them you are writing code that will only work in a game where the scene tree has the nodes you are searching for.

Object orientation is so successful because it helps avoid bugs and create reusable code. If you stick to the rule that methods should only modify the members of the class they are in you are making your debugging sessions a lot easier because you know that if an object ends up in a faulty state it can only be one of the class's methods fault.
The issue with components on the other hand is that the requirements they impose make it harder to test and reuse them, because any such requirement has to be fulfilled by any future project you intend to reuse the component in, and components can't be run on their own. That's especially a red flag in Godot where there is a button that allows you to run and test every scene individually.

You mentioned that referencing nodes especially as the tree mutates was a major barrier, and as well it should. It's because scenes that reference external nodes impose requirements on the scene structure, so changes in the environment could lead to bugs in the scene and therefore affect how the scene itself works. If you stick to aggregation you won't need to touch your scenes unless it's the functionality of the scene itself that is supposed to change. But from the view of someone who intends to use a scene it's completely encapsulated and you don't need to care about how it works apart from the public interface. (disclaimer: I know Godot doesn't have language features to explicitly define a public interface, but semantically it's still there.)

Every engine and every language has its own focus. For example in Unity you can add an attribute to a class declaration to express that one component can only be attached to a GameObject that also has a different component (the RequireComponentAttribute). Unity has that feature because it focuses on components. In Godot such a requirement isn't necessary, because if you want to express that your object should always have a certain combination of children you just create a scene with these children. Instead of explicitly declaring requirements you declare objects and hide those requirements as an implementation detail.

I am a teacher as well and use Godot for my introductory courses, and it's because of the fact that it has more tools that encourage correct use of object orientation (like the "test this scene" button) and less features that help you evade OOP than other engines. If you want to go more into details you can send me a message and we'll talk about our courses some more. I'm always interested in how other teachers teach game development.

I have not dug into the code fully yet so I do not know the structure, admittedly. Is there no way to reference nodes with something like? 'get_root().myRootNode.desiredNode' or 'get_root()->myRootNoe->desiredNode'

$"/root/myRootNode/desiredNode" or get_node("/root/myRootNode/desiredNode")

Let me answer one question that would probably come up next: what do you do if your code needs to call a function in a node that can't be part of the same scene?

Let's say you are writing a player and a UI widget. The widget is supposed to display the player's health, and when the player is hit it needs to be updated. So the player needs to inform the UI, which sounds like a perfect use case for get_tree().get_root().get_node("UI/healthbar") or something similar. So what do you do if you aren't allowed to use get_tree() or get_parent() or get_node("..")?
You use signals. A signal is a function call that is bound at runtime. Declaring a function requires you to specify the code that is run when the function is called and allows you to call that function. Declaring a signal doesn't require you to specify the code but still allows you to call it (resp. "emit" it).
So all you need to do is replace the function that modifies the health bar from inside the player scene with one that emits a signal, connect the signal to the health bar and have it update itself.
That way both the player and the health bar work on their own, and when connected by a common parent scene they also work together.

You can see that principle at work all throughout the Godot API. For example Buttons have signals for when they are pressed, because they were written by people that didn't know anything about your game, so how could they have known which code to run when the button is pressed? That's why they had to use signals instead so that you can choose which function to bind to the function call.

I would add another way, if what you want is really to access a node completely (like if you want to make an AI follow some specific target): export a NodePath variable.
This way, your script doesn't need to know in advance the path, whatever horrendous it may be.
You can then wire this path in the editor instead. I think it's not needed that often, because tons of other communication strategies exist, but I mention it just in case.

as akien said

get_tree().get_root().get_node("myRootNode").find_node("desiredNode")

is equals to

get_node("/root/myRootNode/desiredNode")

(assuming desiredNode exists)

I have not dug into the code fully yet so I do not know the structure, admittedly. Is there no way to reference nodes with something like

You can store node references to variables ^^

@Warlaan

So the player needs to inform the UI, which sounds like a perfect use case for get_tree().get_root().get_node("UI/healthbar")

I think you are overthinking this! Everything in Godot can be connected!

If you need to update a player's HP Bar, just simply create an array that stores the references to that player's node, that join the game.

Then use player[id].hp.set_value(50)

assuming you have "onready var hp = get_node("UI/HealthBar")" inside a player.gd

For updating clientside UI values (HP/Mana Orbs on screen), just create a UI autoload. Then you can use UI globally!

@Dillybob92 Yes, everything can be connected, the question is whether everything should be connected.

I don't mean to rudely imply that you were a beginner, but I know that beginners often focus on getting things to work. But when you want to make a living from creating games it becomes much more important that things don't just work initially for once, but that they keep working even when the project grows in a direction or to a size that you didn't anticipate.

Sure you can store references, but threads about how to deal with references to deleted nodes or nodes that are queued for deletion pop up regularly. And singletons (a.k.a. autoloads) are well known for being very controversial, because they are basically just a workaround that restores global variables in languages that don't have them by design.

If all you want to do is quick and dirty prototypes or small games in your spare time then by all means go ahead and use whatever gets you to your goal, but if you intend to build a professional code base then it's definitely a good idea to understand the implications of the different programming paradigms firs.

If all you want to do is quick and dirty prototypes or small games in your spare time then by all means go ahead and use whatever gets you to your goal, but if you intend to build a professional code base then it's definitely a good idea to understand the implications of the different programming paradigms firs.

My solutions are not quick and dirty prototypes. They conform to the rules of Godot really well, and work great!

Sure you can store references, but threads about how to deal with references to deleted nodes or nodes that are queued for deletion pop up regularly.

Try to not let a Godot developer's fault (not queue_freeing their node and erasing it from an array properly) be a negative conclusion about Godot's potential.

Our game is now reaching just over 27,000 lines of GDScript and not stopping anytime soon. Time is gold, I recommend start working on your game now! :)

Groups are good for things like that, instead of finding random-located nodes with expensive search functions.

@akien-mga
Sure, $"/root/myRootNode".find_node("desiredNode") is a little bit shorter than get_tree().get_root().get_node("myRootNode").find_node("desiredNode"), but it still misses the mark for beginners getting into Godot, and these are folks that shouldn't be ignored.

I mean, if you can write "root/myRootNode" and not even flinch at how obviously counter-intuitive that string is to a beginner, then you've gotten to the point where you have entirely taken it for granted. And writing the name "myRootNode" makes it non-portable. Getting the last child of root is more portable (since that is always the root node, right?), but that's way hairier.

To anyone who challenges a different way of doing something as against the "rules of Godot", you sound like you are far from the mentality of a teacher or are apathetic to beginners. Part of the learning curve toward doing things the "right way" frequently involve doing things the "wrong way" first.

For example, I'm grateful for myNode.duplicate(), because using this to teach spawning nodes makes it much easier to transition to load("res://myScene.tscn").instance(). Of course, using packaged scenes is most times better than cloning a "mother" node, but beginners have trouble abstracting the idea of a branch of a tree in a totally separate scene. Duplicating things within a single scene is much more intuitive. By doing things the "wrong way", we can learn about trees and adding children before tackling the more conceptually challenging part.

Adding a feature like _.desiredNode to find any node in the scene tree won't get in the way of those using Godot the "right way," but can help beginners get off the ground with Godot, making the engine more attractive to teachers. Something like this goes a long way to bring attention to Godot.

I mean, if you can write "root/myRootNode" and not even flinch at how obviously counter-intuitive that string is to a beginner

How is this counter-intuitive? If you mean the name "myRootNode", yes that's confusing, but I'm just using the example node name _you_ provided. Everything is a child of the main viewport, so having the word root in a node name again is I agree, confusing.

Better example would be "root/Login" or "/root/CharSelect"

I don't believe that is confusing or counter-intuitive? In fact, it's quite the opposite, it's extremely easy to access a child nodes location, I'm not sure what would be a better way here?

And about the "beginners", how long beginners stay like that and how helpful for them will be to have an option that will make them do a not-at-all good design?

I think it's better to make tutorials or document for beginners to learn to make a good design for game made with godot, than make easier to get node from root.

http://docs.godotengine.org/en/stable/learning/step_by_step/scenes_and_nodes.html#nodes
maybe it's short description for beginner.

This thread is a good example how difficult it is to identify the best way to program something.

For example Dillybob92 said "Try to not let a Godot developer's fault (not queue_freeing their node and erasing it from an array properly) be a negative conclusion about Godot's potential.", but Godot does offer language features that remove the necessity to erase that node from an array, so blaming the developer instead of using a feature that eliminates one possibility to introduce bugs means not making use of Godot's potential.

Then Oranjoose used duplicate() as an example for a quick and dirty solution when it's simply a different way to create an object. instance() is instantiating a class, duplicate() is instantiating a prototype. Both are valid OOP techniques, that simply result in different objects. duplicate() doesn't make your code harder to read, to maintain or to debug and it's not significantly more costly than instance().
Classes that access the tree arbitrarily do have all those drawbacks. Doing that is not a valid OOP technique, because it makes the code harder to read (because you have to know what kind of node is placed at that position in the scene tree, which you can't tell from what you see in the currently opened scene), it makes it harder to maintain the code (because you need to keep the relations between the code and the scene tree intact - if you move a node you need to check whether it's accessed from somewhere), it makes it harder to debug the code (when an object receives a command at the wrong time you have many more options where the faulty call could be originating from) and depending on what you search for it may be significantly more costly.

@Oranjoose I certainly don't want to question other teachers' abilities publicly like this, but when you say that challenging a way to solve a problem was "apathetic to beginners" or "far from the mentality of a teacher" you are basically doing the same to me, so I don't see why I shouldn't respond to that.
First of all the idea that game development today wasn't beginner friendly enough is ridiculous. A mere 10 years ago pure C++-based graphics engines like Ogre3d were a godsend and engines like Unreal cost upwards of 5-digit figures. Since then there was a change in mentality among beginners and as a consequence in the industry. Lots of people don't use that low entry threshold that we have today to learn all about game development, it merely enables them to make games without using the full potential, and that shows in the results. If creating a game is overly complicated every technique that makes your life easier stands out. The easier it becomes to use any solution the harder it becomes to understand the benefits of the alternatives. So it's not just that game development has become much more beginner friendly, it's that the focus on beginners has shifted the barriers into the area between the beginner level and an advanced level, resulting in developers that see themselves as experienced even though they haven't understood the implications of different workflows.

So I'd argue that giving people some way to identify which workflow may not be the advisable one is exactly the task of a teacher. I mean when you opened the thread you asked why there wasn't an easier way to do this and you got some explanations about the drawbacks of doing so.
We are at a point where getting into game development has become significantly easier than becoming proficient in it, and the effect of that shows in the number of learning materials that contradict many of the rules that were established in software development before and that apparently even tutorial authors didn't feel the need to read because apparently the way they are doing things is "good enough".

To make one thing clear: this isn't about talking down on beginners or punishing people for not doing things "my way". There are obvious benefits to using the right tools and workflows that can easily be explained, so it's always about preventing bugs in the long run and speeding up development, because creating a game may have become much easier, but keeping up a viable business from it is still very hard. You don't want to find out about the drawbacks of your code design one week before the intended release.

For example Dillybob92 said "Try to not let a Godot developer's fault (not queue_freeing their node and erasing it from an array properly) be a negative conclusion about Godot's potential.", but Godot does offer language features that remove the necessity to erase that node from an array, so blaming the developer instead of using a feature that eliminates one possibility to _introduce bugs_ means not making use of Godot's potential.

But he's making that particular point under a false premise. There is no bug! If you queue_free, and run array.remove after, queue_free is synchronous, it will work perfectly fine.

Just because Godot offers language features as you said, I'd argue that means it's really flexible. That's a good thing!

Maybe new developers are coming to Godot thinking GDScript has some omnipotent feature that will magically create your game with no issues? I'm not entirely sure :P

@Warlaan I agree with your sentiments for the most part. I think we primarily disagree about the actual consequences of offering shortcuts for beginners. You're right that it is the "task of the teacher" to show students best practices—the issue is in which context and order. More wholly, a teacher does more than simply "show" best practices, but must help a student _understand_ best practices. There's not many things worse to break a student's learning spirit than pedantically emphasizing concepts that offer no self-evident value or reward to a beginner. "Trust me" is not good enough.

You're right that there was a time not too long ago when virtually all game engines were C/C++, and beginners were compelled test their mettle as a true prospective game developer, learning the hard(er) way. Since then, one may wonder if we are complicit in the race to the bottom in a compromising show of hand-holding. That's debatable and beside the point.

Take Unity for example. They came along at a time where, to beginners, they were like a beacon in a dark world without rich visual editors, mired in C++, and arduous platform targeting. Look what happened: the grand majority of Unity devs are incompetent "good enoughs" that aren't good enough to design a code base that sees through to the end. The important point though is that these beginners and their accompanying tutorials mask the reality that 500 competent devs is more than 10 competent devs, even if the 500 are out of 10,000, and the 10 are out of 100.

The question isn't would beginners ever use workflows that achieve "Godot's potential" if we gave them vapid shortcuts, but rather would beginners ever use Godot, period. It can be the small things that matter. I've been checking Godot every other semester for the last 3 years when considering which engine to use for my class, and I've always declined to bite until this semester. Do you know what finally tipped me over the fence? Two things:
the fact that you don't have to do set_process anymore to enable the _process function,
and that you can increase an x position by 10 by typing position.x += 10 rather than something like set_position(get_position().x + 10, get_position().y) (thank you @reduz for heralding it with those Jan/Feb tweets).

Call it piddling if you like, but each of these small boilerplate things stack onto the learning anxiety of a totally vanilla beginner, and serve as barriers of entry. And I believe that Godot needs to lower the barrier of entry wherever possible to compete in this post-ogre3d world, and it doesn't have to become Construct 3 to do so.
Again, it's not preventing a promising developer from learning the right way to use Godot by offering these shortcuts. On the contrary, _not_ incorporating these shortcuts prevents a promising developer from ever _finding_ Godot—as the hopeless devs wouldn't have touched Godot and pollenated it. I sincerely understand the fear of adulterating Godot and its community, but I don't think it would actually play out that way =).

I think you not getting the same result when using

myNode.duplicate()

vs

load("res://myScene.tscn").instance()

(internal ressources (shader, animation,...) are not unique and get shared)


When I started with godot I created a single scene and put everything inside (no instancing).
Nearly every node has had his own script. So I need to work a lot with get_root() and get_parent().
During time I learned that it is a very bad design.
I think you get this kind of problem also in other programming language until you learn to use design pattern.

Perhaps documentation should have a part for godot design pattern and there should be also some demo project which are constructed that way.

Some of this will be easy to incorporate yourself if you decide to use C# because you can use extension methods. For instance, you can do something like the following (untested code).

``` C#
public static class ExtensionMethods
{
public static Node Find(this SceneTree tree, string nodeName)
{
ViewPort root = tree.GetRoot();
Node result = null;

    for ( int i = 0; i < root.GetChildCount(); i++ )
    {
        result = root.GetChild(i).FindNode(nodeName);

        if ( result != null )
        {
            break;
        }
    }

    return result;
}

}

Then just call this from *any* node in your project

``` C#
GetTree().Find("NodeName");

Let's try to stick to the original topic. Just a couple of comments to hopefully conclude the side topics:

@puppetmaster- You are right, duplicating a node is certainly something else than instancing a class, but both are equally viable techniques. Afaik there isn't a general downside to duplicating a node while there are general downsides to doing what the OP suggested.

@Oranjoose I understand that the numbers you mentioned seem correct out of a gut feeling, but people aren't islands. Let's say that years ago there were 10 good books among 100 others and now there are 500 good tutorials among 10.000 others - then the absolute number of good material would have gone up as well, but it would still be a less desirable situation. And as I said, people aren't islands, they influence each other. It's my experience as a dean that it has become harder to find good teachers, not easier, because bad habits are more common among professional programmers than they used to be.

@Oranjoose I agree that it is good to be able to teach people the worse way of doing something first, before showing them the better ways of doing things, and then demonstrating the advantages and disadvantages with every alternative. However, what you are suggesting in this Issue is to modify the main Godot repo to add a feature which, based on the discussion already conducted, reveals that exact feature to be an act which encourages bad practice. That is to say, using the feature...

add a _"nodeName" syntax to automatically call get_tree().get_root().find_node("nodeName")

...would virtually always be a bad idea as any alternative method of finding the node with "nodeName" as its name would be more efficient, more portable, and/or simplify inter-scene relationships. If the feature itself is admittedly a bad idea, then perhaps a simpler way of doing this should be made...but in a plugin of some sort that doesn't force the bad practice as code bloat in the main engine repository.

If it's a technique that has the explicit purpose of simplifying things for beginners, then it should be something that is downloaded separately...for beginners. No?

Not to mention the fact that you can also do $/root.find_node("nodeName") which is pretty darn short already and is still a bad idea. XD

Even I wanted to add JQuery-like selector syntax to the $-sign previously (#10155), but something like that, which may be useful for beginners to quickly find certain types of nodes, isn't something that really belongs in the main engine. Even if I did create a plugin for it (since it would specifically appeal to web devs adopting Godot), it would still not be as effective a design as the established methods, and thereby would encourage bad programming practices, so I probably won't really move forward with it.

Edit:
Okay, maybe insinuating that beginners should be benefiting from something by having to additionally download and turn on the plugin is a little silly (you'd just be adding steps to their Godot install process after all, making it even MORE complicated), but so is expecting the engine to add a feature that has minimal benefit outside of that exact, very terrible usage, for its own terrible sake. If it isn't meant to be taken seriously as a feature, then it has no business being a feature. What is needed is simply better documentation on how to take advantage of Godot's potential...regardless of whether it's "taking advantage" of it in a good way.

Something I find to be problematic about get_node(path) is that if there's no node with the given path, it silently returns null, instead of throwing an exception that shows the full path in the error message, and tells the user that there's no such node. It's more user friendly than the generic null value exception, which will come as soon as you try to use the node (which may happens elsewhere than get_node, to make things worse). After all, how often do you want to get a node that can be missing by design?

This is somewhat related to the idea of using a special syntax for getting a node ($someNodeName). If you disallow returning null there (as you should have with get_node, IMO), that would mean that you can check if the node name is valid when you load the script (as opposed to when running it, and actually hitting the statement where the null becomes a problem), similarly as you do with variable references that have no corresponding var. This would help maintenance. Referring to nodes with fragile strings could be avoided. Right now, if I want something similarly maintainable, I had to copy the get_node results into top-level var-s (fields) in _ready, and check if neither is null. So then if there's a mistake with the names, it will be revealed in _ready, and _process and such only uses the fields, not get_node. But this is obviously a lot of boylerplate. A special syntax could automate this.

Null nodes happens, because you can have the NodePath stored and for some reason freed/moved, waiting for something to be ready, etc.
That is why is better to do a has_node before operations with nodes that can or can not be on the tree.

But what am I supposed to do if the node is missing? I mean, in 99% of the cases there's no plan B. I have put the node there, because I need it, so the application must be terminated. (I haven't yet figured out how to terminate the application with error... Not that I want to in this case, as Godot should terminate it.)

Besides, isn't calling has_node the same as checking if the get_node return value == null?

I haven't yet figured out how to terminate the application with error

You can just use assert(). This will stop with an error if running in debug mode.

If you have not considered a missing node, that is a bug in the game.

And the last thing you will want is that the engine terminates the program for you, losing the control of the program to that point is bad.

If you have not considered a missing node, that is a bug in the game.

Huh? I'm talking about the case where the node is not optional by design. For example, if you look into the official example projects, it's full of things like get_node("sound").play("shoot"), get_node("bullet_shoot").get_pos(). These don't consider the case when node is missing, and of course they shouldn't either.

That the exported production version just silently ignores errors (by skipping the rest of the _process or whatever the failing method was) is something that I have just discovered, and is beyond my comprehension. Why would anyone do such a thing? Now if I make a bug that some users run into, then the application will just behave strangely (maybe just a bit strangely, depending on where's the bug). Good luck figuring out why's that happening, if you get a problem report at all. If it crashes, it's much more probable that it will be reported, with an attached log if you do it properly. So you have chance to fix things. But this would be a different topic...

Whether or not you design your nodes to be optional is a matter of style. I recommend that people design nodes to be non-optional in most cases, but others will make extensive use of optional nodes and that's a valid solution either.
For example you could have an enemy scene in a side scrolling shooter that may or may not be able to shoot back, so if it does have a weapon attached at a certain path it uses it to shoot, but if it doesn't it just moves. Solving that with nodes has the benefit that other team members can "plug in" a different weapon by just exchanging the weapon node.

Whether or not to crash with a message when something like that happens is a matter of style as well. I prefer to have my games crash as violently as possible as well, so that you get immediate feedback, but it's not unusual to have libraries try to react as silently as possible to bugs, so that if your game ships with a bug it may go unnoticed by the customers.
As a fellow developer I understand that you want your bugs to stand out, but if you think about it from a user's perspective you'll see that if your game does contain a bug and if there's nothing you can do about that you'd prefer the game to try to ignore the error instead of crashing right away because e.g. one particle effect was missing.

Sure, both case need to be supported. But right now, there's get_node that's practical for the optional case, and there's nothing like get_required_node, despite that that's by far the most common case (I believe). I would prefer if get_node is for the required node case, and then there's something like get_optional_node.

As of errors, I have a non-game developer background, and there what you do is showing an error popup or error page (with some information that the user can send to you), and usually the user can OK that and continue work (after the program fell back to a well defined state). I don't know why it should be different for a game, except that you rather not block the action with a popup, but add a flashing "report error" icon in the corner or something. So, I wouldn't crash the game either, but being totally silent is also not a good alternative.

func get_required_node(path)
   var node = get_node(path)
   assert(node != null)
   return node

You're welcome. ;-)

No, seriously, I understand what you mean, but C++ game development is typically a rather conservative branch of software development, so conventional solutions like returning a value that allows you to check whether or not an operation was successful is still the preferred solution in many teams.
For example you still see this kind of code in some code bases:

HRESULT ShowDialog()
{
    IFileOpenDialog *pFileOpen;

    HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL, 
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
    if (SUCCEEDED(hr))
    {
        hr = pFileOpen->Show(NULL);
        if (SUCCEEDED(hr))
        {
            IShellItem *pItem;
            hr = pFileOpen->GetResult(&pItem);
            if (SUCCEEDED(hr))
            {
                // Use pItem (not shown). 
                pItem->Release();
            }
        }
        pFileOpen->Release();
    }
    return hr;
}

(taken from https://msdn.microsoft.com/en-us/library/windows/desktop/ff485842(v=vs.85).aspx)

To answer the question why you can't simply transfer methods from the non-gaming world: Because gamers aren't software users. Typical users use a software to achieve a result. Gamers use a software because using the software is fun. If you want to print a document you will have no problem with clicking OK in a popup window, but if you want to enjoy feeling like an ancient hero a popup is inacceptable for a significant number of players.
I have worked in a large company that produces F2P MMOs, and the mentality there is all about removing anything that might drive people to leave the game. If there's even the smallest inconvenience you'll see the "key performance indicators" drop down. For example in the game I was working on we found that there is a not so small number of users that did register an account and then lost interest before they even started the game. And it wasn't because the game wouldn't run on their hardware or something, these people had finished creating an account but had never clicked the button that would have started the game, so apparently either the page was too confusing or the registration took too long, or whatever other tiny inconvenience kept them from continuing.

So that's why there are only those two workflows: either crash immediately with a loud bang, so you'll notice the bug, or try to continue as if nothing happened hoping that the bug doesn't cause anything worse.

So if you want get_node() to behave differently I do recommend that you write your own wrapper around it. Writing two methods that follow different programming styles is a bad idea since it makes the code base unnecessarily large and is confusing for developers. If there's one standard solution people know how to deal with errors. If they enter a team and see that the code contains custom error handling methods they'll know that those should be used. But if there are two standard solutions you'll very quickly find that one developer on the team uses one and others use the other. Just look at a couple Unity projects and you'll see what I am talking about.

I did write my own wrapper, but it strikes me as lame, as working with nodes is pretty much the halmark of Godot. If you make the best practice the default, i.e., that get_node explodes for a missing node, then only those who do need to deal with optional nodes will look for an alternative... I don't think people will call get_optional_node where they don't need that. It's longer and all.

As of error handling, I have specifically said no popups, but some icon in the corner. But it even can be, like, when you exit the game, it tells you that there were some errors and it would like to send the error logs. Anything but losing the information. There are ways between the two extremes. (That quoted C++ code... they must be masochistic, because it doesn't even report the problems in test builds. Unless, the SUCCEEDED macro does that. But I don't want to derail this issue.)

The current default is a best practice, it's just not what you consider the best practice. Sorry, but programming isn't a natural science, it's a culture. There isn't the one best practice for everyone.

And yes, I read that you said no popups, but that doesn't change much. In my experience even with a popup it's highly unlikely that anyone is going to report the error. And it's even far less likely that anyone is going to report an error because of a blinking icon. If anything the only thing they are going to report is that the blinking icon is annoying and that it should be removed.

At the MMO I had been working on the in-house editor and about half a year after we had finished working on it because there were no serious bug reports any more I talked to one of the level designers and asked how the editor was doing. And he said that it was running great - well, except for that one bug where the editor crashes and they have to delete a file and restart it, but that's fine... There was a bug that caused the editor to sometimes crash during startup (and apparently it didn't happen that rarely either) and instead of walking a couple of meters over to our room in the office they found a workaround and stuck with that. And of course players who don't know the developers and have to go through much more hassle to report a bug will be even way less likely to report something. It's just much easier to turn to one of the many many other games.

So trust me, anything short of ending the game is not going to produce a meaningful number of bug reports. The only thing it's going to accomplish is that your players are annoyed and may badmouth the game, complain to you and/or stop playing it.

Keep in mind that the debugger will break execution once you try to call anything on the returned null, so even if you don't assert the return value you will still be informed.

This discussion is pretty weird. Here's my take on it to clear some misunderstandings

  • get_node() will throw an error if the node is not found, you can see it in the debugger. It doesn't fail silently.
  • As stated above, In 99% of cases you rely on the node being there. You built the scene yourself, so it's not like it's going to disappear.
  • If you want to make code optional depending on the availability of a node, you can call has_node() before get_node in very rare situations where you would would like to check.

I don't really think the API could be further improved, but we could make the documentation better if something was misunderstood.

@reduz: get_node() fails silently if you aren't in debug mode (as far as I can tell). Then the whole "callback" who runs into the null (let's say _process) fails silently, so its second half is not executed. Whether that's desirable is a different topic, but that's what I meant by failing silently.

Other than that, yes, improving the documentation could help in this case. As the game has stopped at the null value, I have fixed the root cause (wrong node name in an earlier get_node call) and haven't realized that there was also an log entry about the missing node from earlier. My mistake. The get_node API documentation doesn't say anything about what happens if the node is missing (that it logs the error, then returns null, and everything continue as normal). It's just now that I realize why's has_node is good for an optional node instead of just using get_node() and then checking if it has returned null.

@reduz
A common case scenario is setting the node paths in the script editor- with autocompletion catching the errors as you type, then much later in the process you or someone else changes the parent/child relationship of a node or renames the node in godot - breaking all scripts that have those paths to it
established.

I've been thinking of implementing an addon that solves the issue,but haven't settled on a solution - not sure how to best approach it.

Godot's script editor could receive signals every time a node has been moved in the hierarchy or renamed- it would really help if it automatically tried at least to update any broken paths for the user in the attached scripts.

Perhaps it could store the node's unique ID with every path, and if a path becomes unreachable, it could try to resolve it by finding the node again by it's id? But then it would need to store the id's somewhere too. Would be cool if the NodePath resource had that ability

Of course having to iterate through all attached scripts and correct outdated paths could be expensive.
You also have the complication of having relative and full paths to nodes - so even approaching it as an addon is hard

Just trying to think of ways around this

@blurymind That should really be a completely separate means of tracking a node, not modifying how NodePaths work. NodePaths state that you want whatever node is at a particular location. You are wanting to locate a particular node, even if that node moves, which is a different concept. What you are describing is actually more like #7821 where people were discussing inventing a NodeRef that you could export and specify is meant to refer to a particular Node reference.

Based on the title, this Issue seems to be about generic find operations. That could only be "improved" if you increased the parameters of the search / what to search for. For example, something more like JQuery which people have already stated they don't want to be included in the engine core (#10155).

I am mostly interested in getting the functionality in the editor. Can it
not be done via the current api?

On Thu, 5 Apr 2018 14:44 Will Nations, notifications@github.com wrote:

@blurymind https://github.com/blurymind There really should be a
completely separate means of tracking a node, not modifying how NodePaths
work. NodePaths state that you want whatever node is at a particular
location. You are wanting to locate a particular node, even if that node
moves, which is a different concept.

That would actually be equivalent to #7821
https://github.com/godotengine/godot/issues/7821 where people were
discussing inventing a NodeRef that you could export and specify is meant
to refer to a particular Node reference.

Based on the title, this Issue seems to be about generic find operations.
That could only be "improved" if you increased the parameters of the search
/ what to search for. For example, something more like JQuery which people
have already stated they don't want to be included in the engine core (

10155 https://github.com/godotengine/godot/issues/10155).

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/godotengine/godot/issues/12541#issuecomment-378940698,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AGMbVd0YpFDpckdJy6V2laGOT5BpWBOQks5tlh-ygaJpZM4QNJ62
.

@blurymind there is not currently any way of having the editor automatically update a NodePath for a node if the node moves around, nor is there any way of specifying the type of a Node assigned at an exported NodePath. That's what the discussion about a NodeRef in that linked issue is all about.

Automatically changing path names when a scene is edited may sound like a good idea when you are using the intended workflow where for every node in a scene there is one distinct script (if any), but some people like to use one script on different nodes in different scenes or even attach and detach scripts at runtime. In these cases it would probably lead to quite inconsistent results if the editor tried to identify which paths should be modified and which one shouldn't.

Perhaps it could be a setting?

On Thu, Apr 5, 2018 at 6:51 PM, Warlaan notifications@github.com wrote:

Automatically changing path names when a scene is edited may sound like a
good idea when you are using the intended workflow where for every node in
a scene there is one distinct script (if any), but some people like to use
one script on different nodes in different scenes or even attach and detach
scripts at runtime. In these cases it would probably lead to quite
inconsistent results if the editor tried to identify which paths should be
modified and which one shouldn't.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/godotengine/godot/issues/12541#issuecomment-379021285,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AGMbVRxcGfWJDQHe4GyOPJkVaQW64LoGks5tllmtgaJpZM4QNJ62
.

@blurymind in my idea of node references, there is not even a need for settings because it would be based on property type. If the property type is NodePath, then it will use a path. If it's a Node type (or NodeRef, whatever), it will be a ref. I explained everything in the issue linked above (https://github.com/godotengine/godot/issues/7821).

I have a different idea which i would like to present:

Giving you the option to make some nodes scene instance variables.
So using the same system than signals do, but add references to scripts instrad of callbacks. Those are than node specific and Don't depend on scene structure changes.

Since they are variables in a script now it is also very easy to access them when inheriting the scene, or when you have a reference to that scenen in a different script.

@toger5 I am extremely confused by what you mean. How would using scripts instead of callbacks be relying on the same signal system? Are you just referring to the syntax of declaring nodes and/or scripts, and then having the editor only allow you to assign THAT node or a node with THAT script? That's basically what the NodeRef concept is all about, resulting in the declaring script having a reference to the Node or ScriptInstance. This would be a scene-structure-agnostic means of tracking a particular node.

Basically it is the same than nodeRef.

I think i know where i was unclear:
I mentioned the whole signal thing to refer to the ui in the editor where you can connect signals and manage groups.
This ui could be used to also setup a nodeRef to a script. Than the node would be marked as referenced (the same it is marked as connected at the moment) And it would behave as a script variable. Maybe like so:

scene var my_referenced_node

I prefer it from a workflow perspective, instead of having to type in export variables, and than go to the inspector to connect the right node, you would just add the node ref and even save the time of typing in the variable declaration. (There also could be drag and drop support)

But @Zylann s proposal is amzing. It also solves a compleatly differnt usecase. Having plugins which rely on node references , a follow camera addon for example which has a target configurable inside the Inspector.

But the two approaches can exist alongside anyways. So if you have a lot of nodes connected via reference but don't ever need to change that reference you just link them the way i proposed and there is no unecassary elements in the inspector. And you still have the advantage that you can change the tree steucture as you want. If you have a script which needs a node reference which is situation based, for example a light which can hover over different characters/npc it can be a coustom secene and the hover target could be assigned via the inspector with the ndoe ref export variable.

@reduz 7 month ago you posted:
"You built the scene yourself, so it's not like it's going to disappear."

One issue i had myself with the current api is while building ui's.
The cleanest way is obviously to use h and v box container to make it adaptive. But i often make changes like adding another button and needing to add an additional layer of h/v boxes. Which breaks node paths.

@toger5 When you say scene var, do you mean that this value is a global variable? Like static class members? Because otherwise I don't see how it is different from just putting "onready var my node = $abc/def" in a script.

No it's the same than a onready war, except handled differently. Instead of having the connection declaren inside the script with a string path, (which u might need ro change) the connection is made inside the editor with a reference to the actual node obj. Basically it is rhe same when the game is running, but faster to set up and easier to maintain when you are working on i in the editor.

Re: @toger5 's comments above I believe I have the same suggestion. Working with the $ selectors, although a big improvement vs Godot 2, is somewhat 'magic string' like in that node references break if renamed in the node tree. In my day-job of CMS development, hard-coding references in code this way could break the site if a CMS editor moves nodes around. And I'd probably have to do things to the dev responsible if the PR got past me somehow :)

Anyway the issue that brought me here today is building a UI in Godot, e.g. menus: wanting to show / hide sections of the UI via GDScript. Or in the case below, working around a bug in RTEs: #18260

# this hack is necessary as RichTextLabel has a bug where it doesn't inherit parent's size
$main/mainVBox/mainSplit/PanelContainer2/VBoxContainer/baseInfoText/rte.rect_size = $main/mainVBox/mainSplit/PanelContainer2/VBoxContainer/baseInfoText.rect_size

If you would be kind enough to set aside for the moment: a) my newness to Godot and b) that I should (and will, when ready) break this down into multiple scenes, my concern is that when I refactor and give the nodes more meaningful names the above code (and anything else referencing them) will break.

If instead, there was a way for my 'rte' node to have an internal ID that I can reference in code, this would make me happy. This'd probably require a search by ID feature in the Scene's node tree too :grin: Thanks for reading my ramblings.

PS. I just had a look over at the dark side and was stunned to discover that Unity doesn't seem to have a means of referencing GameObjects by ID either (there's Object.GetInstanceID but this appears to be generated at runtime).

@christh ty for your comment.
@wmww and I had a little discussion about that issue as well:
We came to the conclusion that the best solution is, that you can connect nodes to export variables. So the path to the node would be stored in the .tscn file and not the script (.gd) file. In that case it would be expected bahaviour, that on renaming or moving a node the link would just get updated in the .tscn so no script would be needed to be updated.

@toger5 So, are we thinking something like this then?

export(NodePath, REF) var link_button # location may change as we re-arrange the UI

Then somehow detect properties of this sort and trigger the drag n' drop behavior in the editor to update the scene file with a new NodePath value based on the changed location?

It looks like part of this functionality is already implemented, based on what I see when I find all references to PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE. I imagine we could create properties like this in GDScript fairly easy with minimal changes to the parser. VisualScript appears to already do a lot of this stuff(?).

Some comments on this issue may need a new one opened, like for rearrange the tree.

For the original, now is just get_tree().current_scene so it may be considered solved?

About finding mysteriously lost nodes (which cannot be found by groups or ID, which at least PhysicsServer can do) I don't know if worth to add a list of objects in the shape of an array instead of a tree to find it...

@willnationsdev yea that is pretty much precisely what i was thinking of :)
Sadly didn't keep updated with godot as closely lately. But it is nice to hear the foundation for that is already there.

@eon-s Yeah, I think between get_tree().current_scene and get_owner() for accessing a sub-scene, this particular Issue is more or less resolved.

As for the concept of having NodePaths automatically update as nodes are moved around in the scene tree at editor-time, I actually just tested it and exporting a NodePath already is doing this in 3.1 @toger5 . If I export a NodePath, assign it in the Inspector, and then move the node around somewhere else, the NodePath remains valid even without any subsequent property updates. Given that, I'm not even really sure what PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE is for since it doesn't even seem to be needed. I suppose it's something specific to VisualScript since that's the only place where it's used for now.

Is there some other use-case of exporting NodePaths that's still relevant to this discussion? The NodeRef concept of exporting node types directly has its own Issue, so, unless there's something else, we can probably close this Issue.

Any updates?

Seems like this issue got pushed to the wayside.

@TheMikirog Please don't bump issues without contributing significant new information. Use the :+1: reaction button on the first post instead.

If you need this feature, it would be better to reopen this as a proposal on the Godot proposals repository (while following the template there).

@Anutrix I believe this ticket is more or less moot. I mean, the thing that bothers OP most is how long it takes get_tree().get_root().get_node("Main").findNode("MyNode") to be written out. However, you can easily replace it with $"/Main".findNode("MyNode") which is perfectly concise. I think this Issue can ultimately be closed in my opinion.

Closing per @willnationsdev's comment.

Was this page helpful?
0 / 5 - 0 ratings