Swapping between Scenes in conjunction with Queue_Free

Godot Version

4.6

Question

To swap between scenes, i had a pretty simple plan. I would spawn a transition scene with a higher Z index to smoothly transition over to the next scene in the game, during the transition, which covers the whole screen, it would either wait until the screen was finished animating via await ani.animation_finished or via timer, and delete itself via queue_free().

From the Title screen

var nextScene = preload("res://Scenes/ExplainScreen.tscn").instantiate()

func _on_start_button_pressed():
	get_tree().root.add_child(nextScene)
	thisScene.queue_free()

to the ‘explain’ screen

var nextScene = preload("res://ToGameScreenWipe.tscn").instantiate()

func _on_next_button_pressed():
        get_tree().root.add_child(nextScene)
		self.queue_free()

to the first transition

func _ready():
	var Anim : AnimationPlayer = find_child("AnimationPlayer")
	Anim.play("Screenwipe")
	await Anim.animation_finished
	var nextScene = preload("res://Scenes/MainGame.tscn").instantiate()
	get_tree().root.add_child(nextScene)
	Anim.play("PostWipe")
	await Anim.animation_changed
	self.queue_free()

to the maingame,

and from the main game,

var nextScene = preload("res://EndScreenWipe.tscn").instantiate()
	get_tree().root.add_child(nextScene)
	var Animatetime = Timer.new()
	Animatetime.set_wait_time(1.5)
	add_child(Animatetime)
	Animatetime.start()
	await Animatetime.timeout
	
	find_parent("GOD").queue_free()

To the second transition

func _ready():
	var Anim : AnimationPlayer = find_child("AnimationPlayer")
	Anim.play("Screenwipe")
	await Anim.animation_finished
	var nextScene = preload("res://EndScreen.tscn").instantiate()
	get_tree().root.add_child(nextScene)
	self.queue_free()

And the ending screen does its thing.

and i have the option to retry:

func _on_retry_button_pressed():
	var nextScene = preload("res://ToGameScreenWipe.tscn")
	nextScene.instantiate()
	get_tree().root.add_child(nextScene)
	self.queue_free()

Now, each of these will work, and work fine.

But the moment it attempts returning to any previously visible screen, it stops working.

I’ve looked around for an answer in multiple different places, but the usefulness of the internet is shoddy these days.

Honestly, im stumped.

Any ideas?

“Stops working” how? What is the problem?

As in, it doesn’t change scenes.
Ill either see a blank screen or nothing happens at all.

Change your calls to preload() to load(). See if that fixes your problem.

Alrighty, gave that a try, all the other scene changes still work just fine
Upon hitting that ‘retry’ button, blank screen.

What happens if you use this:

func _on_retry_button_pressed():
	print("Run retry")
	var nextScene = load("res://ToGameScreenWipe.tscn")
	nextScene.instantiate()
	get_tree().root.add_child(nextScene)
	self.queue_free()

Does it print Run retry in the Output?

Clearly its reaching the script because the scene im in does its queue_free.

But yes, if i put in a print statement, anywhere in that part of the script, (aside from after the queue_free of course) it prints.

And the scene loads, because when i ask it to print the name of the scene, it can tell me

You were not specific about what you were seeing. “blank screen” could mean lots of things.

So what’s it look like in the Remote tab after you press the button?

Can you find the level there? When you click on it, does it look fully loaded?

What happens if you put an

await get_tree().create_timer(5.0).timeout

before the queue_free()

sorry. Blank screen as in, godot’s default ‘void’ color. Its not broken, the game is running, there are just no nodes present to display.

As for the remote window: the scenes show up as expected, the main game node comes in, i see the transition node come and go, and the “EndScreen” node is the only one that remains.

After clicking retry, the “EndScreen” Scene (node) deletes itself, as expected. And im left with the global nodes.

And as for your suggestion,
as expected, it waits 5 seconds before deleting itself.

This doesn’t appear to assign the result of instantiate() anywhere, it just gets lost if that is the case. Is there nothing in the output log about trying to add a PackedScene to the tree?

1 Like

Looks like the problem is here:

func _on_retry_button_pressed():
	var nextScene = preload("res://ToGameScreenWipe.tscn")
	nextScene.instantiate()
	get_tree().root.add_child(nextScene)
	self.queue_free()

When you instantiate nextScene you don’t store the instance. You try to add the preload as a child in the following line but don’t do anything with the instance.

1 Like

I do something with it in the next line, which is adding it to root,
This same exact code adds it to the game and it begins all its associated elsewhere in the game

what are you talking about?

You need to keep the instance as a variable to send forward.

Right now you send forward nextscene, which is var nextScene = preload(“res://ToGameScreenWipe.tscn”)

nextScene.instantiate() is never used anywhere. Or rather, you instantiate a stray node to outside of the scene tree.

I had it moved around a lot

var nextScene = load("res://Scenes/ToGameScreenWipe.tscn").instantiate()
get_tree().root.add_child(nextScene)

even when i just copy-pasted it from another script that does the same thing, it didn’t matter, the results were the same

Have you tried change_scene_to_file()?

get_tree().change_scene_to_file(“res://path_to_scene.tscn”)

Figured it out.

After reading Godot’s resources on scene transitions, it was about the difference between
load() and preload()

the working code looks like this:

var nextScene = load("res://ToGameScreenWipe.tscn")

func _on_retry_button_pressed():
	var reload = nextScene.instantiate()
	reload.ComingFrom = "EndScreen"
	get_tree().root.add_child(reload)
	await get_tree().create_timer(0.7).timeout
	self.queue_free()
1 Like

This suggestion would have worked if you hadn’t separated these two lines

func _on_retry_button_pressed():
	print("Run retry")
>	var nextScene = load("res://ToGameScreenWipe.tscn")
>	nextScene.instantiate()
	get_tree().root.add_child(nextScene)
	self.queue_free()

But you did teach me this,

await get_tree().create_timer(5.0).timeout

Which i didn’t know existed and desperately needed in my life lol
So thank you very much.

1 Like