Can't instantiate a previously freed scene

Godot Version

4.3

Question

I am new to Godot (1 week since I started) and after a few tutorials I tried to learn by creating my own project.

Right now, my Main loads a MarginContainer representing my main menu
image
The script of this container lets me launch a new game by switching scenes :

extends Control

const game_scene: PackedScene = preload("res://game.tscn")

[...]

func _on_label_new_game_pressed():
	var main = self.get_parent()
	var new_game_scene = game_scene.instantiate()
	main.add_child(new_game_scene)

At this point, I want to be able to return to the main menu via a similar method (script in the game node) :

extends Node

@onready var main_menu: PackedScene = preload("res://main_menu_test.tscn")

func _ready() :
	print(main_menu)
	var test_scene = main_menu.instantiate()
	print("Test instantiation:", test_scene)  # Should print the instantiated object, not null
	if test_scene:
		get_tree().root.add_child(test_scene)

func _on_quit_game_button_pressed() :
	var main = self.get_parent()
	var new_game_scene = main_menu.instantiate()
	main.add_child(new_game_scene)
	call_deferred("queue_free") 

However the previous code can’t change the scene, and through debug in _ready(), it seems that main_menu can’t be instantiated.

I feel like there is something I didn’t get, maybe a problem with queue_free() but I can’t find how to make it work while removing the main menu scene.

Is there a better method to do that ?

If you want to change your scenes manually then this documentation is the best teacher:


But if you just want to switch scenes, the engine already implements this behavior in an easy way. You can use:

get_tree().change_scene("res://scenes/my_scene.tscn")

And it does the same thing. There’s actually a node called root in Godot you can access with get_tree().root that sits before your Main node in your case.

If you’re worried about sharing data between scenes, then there’s a way to make a node global / autoload so it can be accessed anywhere, at any time, and keep its state even while switching scenes (great for playing music too): Singletons (Autoload) — Godot Engine (stable) documentation in English

Btw queue_free() means the object is freed before the next frame but not immediately, so you don’t get errors accessing it. Adding .call_deferred() to that means it is delayed another frame.