Reparenting player during scene transition

Godot Version

4.4

Question

How can I reparent the player node after switching scenes? Currently it throws an infinite loop.

#Trigger.gd
extends Area2D

@export var scene:String
@export var spawn:String = "SpawnPoint"

func _on_body_entered(body: Node2D) -> void:
	if body is Player:
		if scene:
			SceneSwitcher.player = body
			SceneSwitcher.switch_scene(str("res://scenes/", scene, ".tscn"))
#SceneSwitcher.gd
extends Node

var current_scene:Node
var player: Node

func _ready() -> void:
	var root = get_tree().root
	current_scene = root.get_child(root.get_child_count() - 1)
	
func switch_scene(scene_path):
	var new_scene = load(scene_path).instantiate()
	get_tree().root.add_child(new_scene)
	player.call_deferred("reparent", new_scene)
	current_scene.queue_free()
	current_scene = new_scene
	get_tree().set_current_scene(current_scene)

When reparented does your new scene place the player in another Trigger.gd area?

No it places the player in another scene. I have two scenes called level1 and level2. The game starts on level1.

My goal is to move the player to the location of the spawn point, but it crashes before then.

Does level 2 have any Trigger.gd areas within it?

Yes level1 and level2 both have instances of Area2D that use the Trigger.gd script.

I made that Area2D into a reusable scene.

Is “SceneSwitcher.gd” an autoload? Or is it inside the main scene?

SceneSwitcher is an autoload script

From these two snippets of code the only way I see a infinite loop error occuring is from the new scene instantiating another Trigger.gd on top of the player, are you absolutely sure the player won’t occupy the second Trigger.gd space when instantiated?

I removed the area2d from level2 and it still crashes like before.

However if I remove the player from the current_scene manually and add it to the new_scene it does not crash. See below:

Whenever I remove the player from its parent like player.get_parent().remove_child(player) it freezes and crashes.

func switch_scene(scene_path):
	var new_scene = load(scene_path).instantiate()
	get_tree().root.add_child(new_scene)
	current_scene.remove_child(player)
	new_scene.add_child(player)
	current_scene.queue_free()
	current_scene = new_scene
	get_tree().set_current_scene(current_scene)
1 Like

I noticed that the _on_body_entered event is called twice whenever I reparent the player. Is there a way to only call it once?

func _on_body_entered(body: Node2D) -> void:
	if body is Player:
		print("switch scene")
		body.call_deferred("reparent", SceneSwitcher.current_scene)