Okay I solved it.
The big problem is that Godot does not like reusing the same PackedScene again and again after being thrown away via changed_scene_to_packed()
because although the PackedScene is gone, it’s actually still in the cache while the game is running.
Therefore we will switch from loaded packed scenes into loading the resource and then instantiating the scenes everytime we require them. Think of the resource as the blueprint and then we mold it into a scene everytime we load the scene. Additionally, I abused the TransitionLayer as a central place to load scenes, therefore TransitionLayer knows the location of all scenes.
Therefore please refer to the following code
# transition_layer.gd
extends CanvasLayer
# Variable holds the currently loaded scene as a reference
var current_scene
# Variable that holds ALL scene paths
var SCENES: Dictionary = {
"outside" : load("res://scenes/levels/outside.tscn"),
"inside" : load("res://scenes/levels/inside.tscn")
}
# We force the TransitionLayer to spend as much as time as it wants until all SCENES are loaded
func _ready() -> void: await self.ready
# Utility function that implements the transition to a new scene with a proper transition animation
func transition_to_scene(levelname: String) -> void:
if SCENES[levelname] == null:
push_error("Levelname: ", levelname, " does NOT exist!")
return
$AnimationPlayer.play("fade_to_black")
await $AnimationPlayer.animation_finished
# We make a switcheroo of scenes. First add the new scene to the root, then remove the old scene.
var instance = SCENES[levelname].instantiate()
get_tree().root.add_child(instance)
get_tree().root.remove_child(current_scene)
current_scene = instance
$AnimationPlayer.play_backwards("fade_to_black")
# title.gd
extends CanvasLayer
# On startup, the TransitionLayer is told what the loaded scene is
func _ready() -> void: TransitionLayer.current_scene = self
func _on_button_button_up(): TransitionLayer.transition_to_scene("outside")
# outside.gd
extends BaseLevel
# Signal receptor for when player enters the building gate
func _on_gate_player_entered_gate():
var tween: Tween = create_tween()
tween.tween_property($Player, "speed", 0 , 0.5)
tween.tween_callback(func(): TransitionLayer.transition_to_scene("inside"))
# inside.gd
extends BaseLevel
# Signal receptor for when player exits the building gate
func _on_exit_gate_area_body_entered(_body):
var tween: Tween = create_tween()
tween.tween_property($Player, "speed", 0 , 0.5)
tween.tween_callback(func(): TransitionLayer.transition_to_scene("outside"))
For the future, you might need an autoload the holds the position of the player or some stats. You might also want to think about where to “place” the player after the scene loads, right now the player always spawns in the same position.