newbie112
newbie112

Reputation: 45

Godot: Node not found

Hi I'm currently making a game that houses minigames with Godot. After I finish the first minigame, that minigame can only be run when I play that specific scene.

When I try to run my whole project(starting from the main menu screen), it throws me an error(Attempt to call function 'get_node' in base 'null instance' on a null instance) saying my minigame node was not found. So I press the 'remote' button next to 'local' and see that my minigame scenetree is not there and that's why it cant detect the node.

I have tried adding the minigame scene as an autoload. This made it work and I can run it from the main menu again but then the things inside my minigame wont load so it became a blank screen when it arrives to the minigame scene.

How do I make it so my minigame will load properly and the project can be started from the main menu scene?

Upvotes: 2

Views: 5482

Answers (1)

Theraot
Theraot

Reputation: 40210


New Answer

So your code in Autoload is trying to access something that is not in Autoload.

As I said in the old answer, There is no guarantee that what the Autoload is trying to access is there. You cannot rely on it being loaded and available to the Autoload.

Of course, if you run the game form a scene, you could trust that particular scene is loaded. But if you start the at another scene, then that isn't the case. So what do you do in that case?

Well, you need to load the scene you need by whatever means (see old answer for ways to load a scene). And you need to pass the instance to Autoload.

One way to pass that instance, is to have a function in the code in Autoload that takes the Node as parameter. Then after you load the scene, you can call that function passing the instance.

To reiterate, your main scene will load the minigame scene (by whatever means), so it have an instance of it. Then it will get the Autoload, and call a function on it passing the instance.


Let us try again, in your main scene, you will load the minigame. That can be accomplished by different ways. For example, calling load with the resource path will give you a PackedScene. Then you call instance on the PackedScene which gives you an instance.

Afterwards, you can add that instance to the scene tree using add_child...

That same instance you will pass it to the Autoload. The code in the Autoload needs a variable to store it, which you will set.

In your main scene you can use get_node to get the Autoload (and yes, an Autoload can be a scene). See Singletons (AutoLoad). And then set the variable with instance you loaded.


For completeness sake, I'll also mention that you can change scenes with change_scene. The code in Autoload would remain when you do that. And this could make changing scenes simpler. However, if you later need to make something that was a separate scene a part of a bigger one, or if you decide that you need background loading, then having relied on change_scene will be an obstacle. See also SceneTree and Change scenes manually.


Old answer

First some organization. Where are you using get_node?

If you are using get_node in the the main scene to access the minigame. That is ok, you just need to make sure that the minigame is already loaded and in the node path you need it.


Remember that to use get_node you need a Node to begin with. Thus, either the code where you call get_node is a in a Node, or some other code gave a Node (e.g. as a parameter on a function call) to it. Also, what you are trying to get must be there.

These are the problematic cases:

  • The code in the minigame is trying to access something else in the minigame. For this use relative paths, so that they work regardless of where the minigame is loaded. Similarly, if the main scene is accessing something else form the main scene, that is ok.
  • The code in the minigame is trying to access something in another scene (e.g. the main menu). Avoid doing this. Use signals for this kind of cummincation instead. Accessing the Autoload in the minigame is ok.
  • The code in an Autoload is an scene (yes, you can put a scene in Autoload) trying to access something that is NOT in Autoload. Avoid doing this. There is no guarantee that what the Autoload is trying to access is there.
  • The code in an Autoload is using get_node on a node some other code gave to it. You probably can have that other code give the node the Autoload needs directly.

See Node communication and Understanding Node Paths.


Second, there are other things that you could be presuming to be true in your code, that might not be once it is loaded somewhere else. Such as:

  • What camera is the current camera. You may have to change cameras when you load the scene, or to consider the position of the current camera in your code.
  • That the position of the root of the scene is at the global origin. You may need to distinguish global and local position, or set the position of the scene when you load it.

And third, there are many ways to load a scene, for starters:

  • You can, of course, simply add the scene to the scene tree. This is the simplest way to do it (I remind you that you can set visibility). This adds to the load time of the scene. Always start with this option, and only move to something else if it gives you problems.
  • You can also put it on Autoload. Autoload is good for things that should be accesible no matter when or where. I doubt that is the case of the minigame. This adds to the load time of the game.

These methods have one important drawback: the minigame is loaded preemptively. Which may make loading the game slower. If load time is a problem, you might prefer to load it on demand. For that we have more options:

  • You can get a PackedScene of your minigame in code using load (passing the resource path as parameter) and then instance, and add_child.
  • You preload (passing the resource path as parameter) to get the PackedScene. And then when you need it, you can instance and add_child.
  • You could instead use class_name (see Register scripts as classes) which would let you instance it with new. If it is a node, you can add it to scene tree with add_child afterwards. It might not be a node, which is also useful sometimes. By the way, you can give it static functions, which you could call without instantiating it (no static variables, if you want static variables use Autoload instead).
  • You can put the scene on the scene tree, as a place holder. On the Scene panel (on the left of the editor by default), open the contextual menu (right click) on the scene node, and select "Load as Placeholder". This results in a InstancePlaceholder node. You can call replace_by_instance on it when you need it.

Drawback? When loading on demand, the game will halt while loading. If the individual things you are loading on demand are small enough, that is no problem. However for a large scene, it might be. That leads us to a final option:

Drawback? It is significantly more code in comparison with the other approaches. Ideally you would reuse it. In fact, you can put this logic on a Autoload, and that way it is available anywhere you need to load something.

You could, in fact, use background loading to load the main menu. Then the main menu can load all it needs to work simply having it there in the scene tree. Which would also mean that the main menu is not the starting scene. Instead the starting scene uses background loading to load the main menu. Visually it could have a logo and a progress bar, or something along those lines. However, this suggestion is not for every game, only for those that take considerable time to load.


In case of confusion: Node paths are in the scene tree, see get_node and NodePath. And resource paths are in the project internal file system (the ones that start with res://), see Data paths.

Upvotes: 1

Related Questions