How to change scenes in godot

Created by Israel Ruiz on 15 Dec 2020




How to change scenes or levels is one of the most popular questions when it comes to learning game creation. That’s because it applies to all games! I’ll show you how to implement scene changing in a way that will work for all your games. :)

The two ways of doing this are get_tree().change_scene(‘scene file path’) and get_tree().change_scene_to(load(‘scene file path’)) change_scene() loads the file into a PackedScene for you, meaning change_scene() and change_scene_to() are effectivly the same thing. From what I’ve found there are not any clear performance benafits so I use change_scene() because its shorter and more readable.

Basic Game Setup

Now that we know how to change Scenes let’s see how we can implement it. First, we’ll need an actual game to work on. I’m using “Shadows’ Glitchy Adventure.”

The SceneTree is pretty simple. First, we have the root node, Level_1, with a script attached. Beneath that, we have a StaticBody that acts as the floor. Underneath the root node, we have Shadow, the player. Next, we have an Area2D node that will send a signal to the root node when the player area collides with it. I renamed it from “Area2D” to “ChangeSceneTrigger.” Connect the “ChangeSceneTrigger” area_entered() signal to the root node.

Click the ChangeSceneTrigger node. Click the node tab. Select the area_entered(area: Area2D) signal then press “Connect..” Select the node you would like to send the signal to, this can be any node with a script. Click “Connect…”

Click the script icon on the root node. Your script should have a new function! Also, you’ll see the ChangeSceneTrigger node now has a signal icon.

One last thing. Make sure your player has an Area2D that is in the group “PLAYER”. Select the players Area2D node and move to the “Node” tab. Select “Groups”. Type in the name of the group you want to add the node to, then press “Add”.

The Scene Changer

With the preliminaries out of the way, we can get to the fun stuff.

Create a new scene with a CanvasLayer Node, rename it SceneChanger. Attach a script and give it the same name. I prefer saving .tscn and .gd files in the same folder but you can do it incorrectly if you want. Add these Nodes and rename them accordingly.

There are two parts to making scene transitions. The animations and the change scene function call. The sequence of events will look like this: Cover the screen with animation, change the scene, uncover the screen with another animation.

This is difficult to describe in a blog post but the basic idea is to make an intro and outro animation. The two most basic transitions are Wipe and Fade. We will need two versions of whatever transition you decide to got with. Cover screen animation and uncover screen animation. For my transitions, I’m using the ColorRect node. You could also use a Sprite or an AnimatedSprite to achieve different effects and animations but this will work for now.

Select the AnimationPlayer node. You’ll see the animation editor popup at the bottom of the screen. Next, select the node you are going to cover the screen with. In this case, the ColorRect node. Click the “Animation” button that’s next to the animation list. Click “New” in the popup menu. Name the animation and press Enter on the keyboard or “Ok” on the screen.

You’ll see in the Inspector a bunch of key icons appear next to the node properties. If a property has a key next to it then you can animate it. Just press the key icon and the property’s value will be keyframed in the timeline. You can see in the graphic below the only value I’m animating is the “rect_position”. This is the “uncover screen animation” we were talking about earlier.

IMPORTANT!!! Always set Mouse Filter on Control nodes to "Ignore" unless otherwise needed. Set Mouse Filter to "Ignore" on both the Control and ColorRect nodes. If you don't, mouse clicks will be ignored on other nodes, and that will be a big problem later when making menus. 

Scripting

Now that we have the animations done, we can get to my favorite part, scripting! This script will be fairly straight forward. And remember, there are multiple ways of accomplishing the same goal so feel free to experiment and try new things.

extends CanvasLayer

onready var NAnimation := $Control/AnimationPlayer

func change_scene(new_scene, in_anim, out_anim):
    AnimationN.play(in_anim)

It’s a best practice to use a reference to a node in a script. For one, if the node reference is very long it will muddy up the code making it hard to understand. Second, if you need to change the node order you will only need to change one variable. Not to mention it will increase performance! Just don’t forget to use “onready”, otherwise, Godot will try saving the node reference before it’s added to the scene tree.

I name node references with an N as a prefix followed by the node name in UpperCamelCase. It makes using auto typing much easier and keeps things consistent.

I name functions in snake_case, with a slight change depending on if it’s private or public. If I’m going to call the Function from another node, I don’t put an underscore in front, “func public_function()” If it’s a private function, meaning I don’t call it from another node, I would type it like this “func _private_function()”

When we call the function from another node, we send it values that become variables we can use in our function. The three variables here are the file path to the scene we want to change to, the name of the “cover screen animation”, and the “uncover screen animation” You’ll see what it looks like to call this function later. For now, let’s finish writing the script. Before we change scenes, we need to cover the screen. Use the play() function and pass it the in_anim variable. AnimationN.play(in_anim)

Yielding and Coroutine

If we add our change_scene() function before the screen is fully covered the effect will be ruined. To stop the change_scene() from running before the animation is finished playing we’ll use a coroutine. In Godot, it is the yield() function. The yield() function stops the function it’s inside of from continuing until either the resume() function is called or the yield() function is passed an object with a signal. Two ways of using the yield() function.

func _ready() -> void:
    _print()


func _input(event):
    if Input.is_action_just_released('ui_accept'):
        _print().resume()


func _print():
    yield()
    print('end of script')


func _wait_for_anim_to_finish():
  NAnimation.play('anim')
  yield(NAnimation, 'animation_finished')
  print('anim finished')

Since we want the function to continue after the animation is finished, we’ll use the second method. First, we give the yield a node reference. Any signals that are on this node we can use to trigger the function to continue running. We’ll use ‘animation_finished’. All the signals you can use are displayed in the node tab. The tab we used to connect the “ChangeSceneTrigger” area_entered() signal to the root node. Now that the screen is covered, we can change the scene without anyone seeing it happen. Cha-cha real smooth.

extends CanvasLayer

onready var NAnimation := $Control/AnimationPlayer

func change_scene(new_scene, in_anim, out_anim):
    AnimationN.play(in_anim)
    get_tree().change_scene(new_scene)
    AnimationN.play(out_anim)

Making SceneChanger into a Singleton

Finally, we’re done with the difficult part. Just a couple more steps. We are now going to turn our beautiful SceneChanger into a Singleton. Why are we doing this?

In the game, the root node is “Level_1”. When we call change_scene() Godot deletes this node and replaces it with the new one. If we had our SceneChanger underneath “Level_1” it would get deleted and the animations wouldn’t play. Singletons on the other hand are outside of the SceneTree. Another great thing about Singletons is that we can call functions and access variables on them from anywhere. Making them perfect for what we’re doing.

Go to Project -> ProjectSettings…

Select the AutoLoad tab, then the folder icon.

Use the editor to select the node you want to turn into a singleton. Click “Open”. You’ll be brought back to the AutoLoad tab.

Next to the “Add” button, you’ll see a text editor called “Node Name”, this is the name that we’ll be using to refer to this node in other scripts. SceneChanger is a good, name so we’ll leave it as is. Click “Add” then “Close”.

Using the SceneChanger

Now all that’s left to do is implement it in the game. Remember adding the Shadows area2D to the PLAYER group? This is where it comes into play. When an area2D node enters the trigger area it calls the area entered function and gives us a reference to the node that entered.

Use is_in_group() to check if the area entered is the player. Lastly, we call the change_scene() function that’s on the SceneChanger Singleton. I give it the file path to the scene we want to change to and both outro and intro animations. That’s it!

func _on_ChangeSceneTrigger_area_entered(area):
  if area.is_in_group('PLAYER'):
    SceneChanger.change_scene('res://scr/Levels/Level_2.tscn', 'wipe_in', 'wipe_out')

Thanks for reading! If you enjoyed this tutorial and want to help me make more you can buy “Shadows’ Glitchy Adventure” on Itch.io or donate me a coffee. Thank you! Here’s to more tutorials! 🍻