"Node Not Found" even though the node path prints twice. Why?

Godot Version

4.2

Question

I have 2 Questions:

  1. Why does on_body_entered run twice?
  2. Why is the ‘node not found’, despite both print statements show the path twice?
extends Area2D

@export var next_level : PackedScene
@export var player_path : NodePath
@export var camera_path : NodePath

func _ready():
	self.body_entered.connect(_on_body_entered)

func _on_body_entered(body):
	
	if body is CharacterBody2D:
		var root = get_tree().get_root()
		var old_scene =  root.get_child(0)
		var next_scene = next_level.instantiate()
		print(player_path)
		var player_instance = get_node(player_path)
		var camera_instance = get_node(camera_path)

		root.add_child(next_scene)
		player_instance.reparent(next_scene)
		camera_instance.reparent(next_scene)
		old_scene.queue_free()

Error

E 0:00:07:0317   Door.gd:17 @ _on_body_entered(): Node not found: "../Player" (relative to "/root/Root/OverWorld/Door").
  <C++ Error>    Method/function failed. Returning: nullptr
  <C++ Source>   scene/main/node.cpp:1638 @ get_node()
  <Stack Trace>  Door.gd:17 @ _on_body_entered()
                 Door.gd:21 @ _on_body_entered()

Console shows this twice when the player path is printed:

../Player

I definitely think that the fact you get the event twice is what you want to focus on, since during the first event you reparent player, so it makes sense that for second event it can’t find it.

“if body is CharacterBody2D”: could it be that 2 characterBody2Ds are entering? Do you still get code executed twice if you change it to:

if body is Player

(or w/e the name of your player class)

1 Like

There’s only one CharacterBody2D in the entire project.

I tried to change it to if body is Player, since player is the name of my player script, But I get an error in the script editor saying ‘Could not find type “Player” in the current scope’

Is there a better way to determine why it runs twice?

It’s hard to know without seeing all related parts.

Does the body have multiple collision shapes (like main collision shape + area2d with a collision shape) that may be reacting to this?

Screenshot 2024-01-03 090329
This is the main scene. The script above is on door.

1
This is the player scene

This error means that it is looking for a Player class and could not find it.
You can add:
class_name Player
to your player script but it likely won’t solve your issue.
To me it sounds like something other than what you are expecting is firing the event; something other than player.
Drop a print statement at the top to see what body is:
print(body)

It could also be that your reparenting is not working as you expect.
Check what the parentage is before and after reparenting.

I am going to also add that doing a scene change inside the door script is imo less than ideal.
Consider firing a signal and doing the scene changes in the main script or even better in a scene manager script.
Try to envision your objects as what they actually are. Does a door contain a player? No. (Similarly a player doesn’t contain a door). The player interacts with the door.
imo the door script has no business having a player object as a member nor should it have the next scene as a member.

Adding class_name Player to the top of the Player script did not fix the “Could not find type “Player” in the current scope.” error. I do agree it wouldn’t have solved my issue anyhow.

here’s what printing body at the top of the function gives me:

TileMap:<TileMap#74977382281>
Player:<CharacterBody2D#74910270695>
Player:<CharacterBody2D#74910270695>

The tilemap one prints at the start of the scene.
The two player ones both print when the player enters the Area2D

I tried to check the parentage by printing the players parent before and after using

		print(player_instance.get_parent())

but these never print. I’m just receiving the ‘node not found’ error.

I am going to also add that doing a scene change inside the door script is imo less than ideal.
Consider firing a signal and doing the scene changes in the main script or even better in a scene manager script.

I’m not sure how I would go about doing the scene changes from a script that wasn’t the door tbh.

Try to envision your objects as what they actually are. Does a door contain a player? No. (Similarly a player doesn’t contain a door). The player interacts with the door.
imo the door script has no business having a player object as a member nor should it have the next scene as a member.

I am still learning how to do things in Godot. I put the player and the next scene in the door script as references in an attempt to make it easier on myself while learning (so much for that). The idea was to, once I figured this out, change the code so it found the player automatically without needing to drag drop it into the reference. You say do doors contain players? I also say no. But then you admit the player needs to interact with the door, and I get confused when I think about that.I don’t understand how you make the player and door interact without one referencing the other at some point.

When you then say “imo the door script has no business having a player object as a member nor should it have the next scene as a member.” I’m confused by both those things. How wouldnt you need the player object to interact with it, and how wouldnt you need the next scene to switch to the next scene?

these never print

So player_instance is the issue here then.
player_instance should be the same as ‘body’
So you don’t need player_instance.
Try reparenting ‘body’

Also have a look at the tree structure by pressing ‘remote’ (over the scene tree window) while the game is running.
That player path should have worked so the tree is not building the way it seems.
I would agree with alextheukrainian that the code went through once and removed player from the scene but in that case the parentage would have at least printed once ie player_instance would have been valid at least once.
Also try commenting out the reparenting and look at the scene tree after adding the next scene. You might see something amiss there.

I deleted a reply that was somewhat offbase.
But here is a scene manager tutorial

1 Like

I got rid of the player reference, since I already have the body and don’t need it.

now I receive several hundred print statements and errors :confused:

Screenshot 2024-01-03 170401

I’m having trouble filling the scenemanager dictionary in the link you provided. I made a new scene called SceneManager, and put the scenemanager script onto the root node. I also added that scene to autoload. But i’m unsure what the key and value types are supposed to be for each entry.

On the page it says: "The SceneManager node holds a dictionary with scene aliases as keys and paths to scenes as values. "

when I click little pencil next to the key/value fields, I don’t see a type for ‘scene alias’, and there is one for ‘node path’ so my guess is thats the ‘paths to scenes’, but I cant seem to drag/drop my scenes from the filesystem into it.
So i’m stuck as far as using the scene manager technique described in the article.

In an attempt to move on, I decided to ignore the player for now and just comment out the player relevant bits and try bringing only the camera into the new scene.

Here’s how the door script looks now:

extends Area2D

@export var next_level : PackedScene
@export var camera_path : NodePath

func _ready():
	self.body_entered.connect(_on_body_entered)

func _on_body_entered(body):
	if body is CharacterBody2D:
		var root = get_tree().get_root()
		var old_scene =  root.get_child(0)
		var next_scene = next_level.instantiate()
		#var player = body

		root.add_child(next_scene)
		#print(player.get_parent())
		#player.reparent(next_scene)
		#print(player.get_parent())
		
		var camera_instance = get_node(camera_path)		
		print(camera_instance)
		camera_instance.reparent(next_scene)
		
		old_scene.queue_free()

And what happens now is the scene switches only if I walk in, out, and back into the Area2d, and gives an error. The player walking into the area2d triggers the print and shows the camera isnt null, but nothing happens. If I leave and walk into the Area2D again, the print statement says ‘<Object#null>’ and I receive the error “Attempt to call function ‘reparent’ in base ‘null instance’ on a null instance.”

If I comment out all the camera stuff as well as the player stuff, I have to, again, walk into the area2d, walk out of it, and then walk back in - but in this case, the scene does change to the new correct scene.

This is kind of becoming a mess. I like the system in your link, so I’m gonna spend some time later trying to figure out how to make it work so I can use that to switch between scenes instead of whatever it is I’m currently doing.

But the whole point of what I was trying to accomplish was to bring nodes from one scene into another, in this case the player and the camera, and I don’t really understand how I would manage to do that with the scene manager system in the link.

Edit: I figured out the dictionary and the scenemanager now works. The types are supposed to be “StringName”.

I suspect there is the same problem with the camera as you had with the player.

If that camera is a player-follow camera then it should be contained in the player scene and not a sibling of the player.
The most common scenario in 2d is a player character controlled by user inputs that moves around a world and is followed by a camera.
Making the camera a child of player would make it automatically follow the player.
But in any case you can once again print test the validity of that camera node and see if it is getting an actual node.

A problem with the camera might be giving you those viewport errors but it seems to me that something is parked right on top of the Area2D and is repeatedly firing the _on_body_entered() signal.

I had this typed out once before but it wasn’t the problem so I deleted it.
You should manage your collision layers.
Select the Door Area2D and look at the inspector under Collision. There are two layer categories; Layer and Mask.
‘Layer’ is the layer upon which this Area2D can be found by other object collision checks.
‘Mask’ is the layer that this Area2D checks for collision.
Since this is a door the only thing that this Area2D needs to monitor for is the player.
Change the ‘Mask’ layer to 2 and remove 1.
Now this Area2D will only fire collisions with objects that have a Collision Layer 2.
Change the players Collision Layer to include 1 and 2. Now the player effectively resides on both layer 1 and 2 and the Door Area2D can pick up on collisions with it.
This should eliminate any other object collisions with the door as long as those objects do not also reside on Layer 2.

I missed one of your edits telling me to use the remote feature for the scene tree. From what I can see, the new scene is instantiated and the old scene is never freed. On top of that, it’s actually creating the new scene a second time. But The camera2d and player do reparent to the first new scene. So I think my node not found error is from it trying to reparent things to the second new scene, as alextheukrainian said. I tweaked the code more and adjusted my layers as you’ve suggested and now I don’t receive the countless errors any more. But I do still receive just the single ‘node not found’ error.

extends Area2D

@export var next_level : PackedScene
@export var camera_path : NodePath

func _ready():
	self.body_entered.connect(_on_body_entered)

func _on_body_entered(body):
	if body is CharacterBody2D:
		print("firing")
		
		var root = get_tree().get_root()
		var old_scene =  root.get_child(0)
		var next_scene = next_level.instantiate()

		root.add_child(next_scene)
		
		var camera_instance = get_node(camera_path)		
		camera_instance.reparent(next_scene)
		body.reparent(next_scene)
		
		old_scene.queue_free()

So I suppose that answers my second question about why the ‘node not found’ error appears.

I still have no clue about my first question about why on_body_entered runs twice.

You can use Breakpoints to see what the scene looks like when your code arrives at a line.

Simply open the script editor and click on the left of your line, you should see a red circle appear. Why don’t your place it on the “Body Entered” part.

I’ve decided to abandon the idea of bringing nodes from one scene into another scene. I didn’t think this would be such a long and frustrating process.

Did you figure it out I’m having the same issue I feel like I have to abandon the project and start over.