*You can find the links to the previous parts at the bottom of this tutorial.
One of the core pieces of any game is a Pause and Main Menu. The Main Menu is the first screen that your player will see when they start the game, and the Pause Menu will pause the game when the player presses a button such as “ESC”. In our game, we want our Main Menu to allow the player to start a new game, load a saved game, or quit the game entirely. In our Pause Menu, we want the player to be able to resume their level, save their current level, reload their current level, and quit to the main menu screen.
STEP 1: PAUSE MENU UI
In our Player scene, let’s add a new CanvasLayer node called “PauseMenu”.
Add a ColorRect node to it and call it “Menu”. Change the anchor preset of this menu to be full-rect, and change its color to #834040.
Add another ColorRect node to your Menu node, and call it “Container”. Change its color to #964545, its size to (x: 1078, y: 580), and position to (x: 35, y: 25).
Copy and paste the TileMap, AnimatedKing, and AnimatedPig nodes from your GameOver node into your PauseMenu/Menu node.
Let’s change the AnimatedPig animation to be all the frames from the “res://Assets/Kings and Pigs/Sprites/06-Pig Hide in the Box/Looking Out (26x20).png” spritesheet. Change the FPS to “4”, and leave the looping on.
Change the AnimatedKing animation to be all the frames from the “res://Assets/Kings and Pigs/Sprites/01-King Human/Idle (78x58).png” spritesheet. Change the FPS to “11”, and leave the looping on.
Make the TileMap a bit different and move your King and Pig to where you want them to be.
Now, to your PauseMenu/Menu/Container node, add four Button nodes and one Label node. Organize and rename them as indicated in the image below.
1. Label
Select your Label node and change its text to “Pause Menu”. Its font should be “QuinqueFive”, and the size of the font should be 35. Also change its position to be (x: 299, y: 20).
2. ButtonResume
Select your ButtonResume node and change its text to “Resume Level”. Its font should be “QuinqueFive”, and the size of the font should be 25. Change the font color to #d98c8a. Also change its size to (x: 500, y: 80) and its position to (x: 285, y: 100).
3. ButtonSave
Select your ButtonSave node and change its text to “Save Level”. Its font should be “QuinqueFive”, and the size of the font should be 25. Change the font color to #d98c8a. Also change its size to (x: 500, y: 80) and its position to (x: 285, y: 200).
4. ButtonLoad
Select your ButtonLoad node and change its text to “Load Level”. Its font should be “QuinqueFive”, and the size of the font should be 25. Change the font color to #d98c8a. Also change its size to (x: 500, y: 80) and its position to (x: 285, y: 300).
5. ButtonQuit
Select your ButtonQuit node and change its text to “Quit Level”. Its font should be “QuinqueFive”, and the size of the font should be 25. Change the font color to #d98c8a. Also change its size to (x: 500, y: 80) and its position to (x: 285, y: 400).
Now, connect all of your four buttons’ pressed() signals to your Player script.
Change your PauseMenu node’s visibility to be hidden.
Finally, we need to change our PauseMenu node’s Processing Mode to “When Paused”, since we only want it to accept input when the game is paused, and no other nodes need access to it when the game isn’t paused.
STEP 2: MAIN MENU UI
Copy your PauseMenu node branch from your Player scene, and create a new scene from the option “Paste From Clipboard”.
Rename the root node to “MainMenu”, and save your scene as “MainMenu” underneath your Scenes folder.
Now, delete one Button node, and rename the remaining three as indicated in the image below.
Attach a new script to your MainMenu scene, and save it underneath your Scripts folder.
Disconnect and reconnect the pressed() signal connections on all three of your buttons so that they are connected to your MainMenu script.
Then, change your MainMenu root node’s Processing Mode to “Always”.
1. Label
Select your Label node and change its text to “Castle Climber”. Its font should be “QuinqueFive”, and the size of the font should be 35. Also change its position to be (x: 299, y: 20).
2. ButtonNew
Select your ButtonNew node and change its text to “New Game”. Its font should be “QuinqueFive”, and the size of the font should be 30. Change the font color to #d98c8a. Also change its size to (x: 500, y: 100) and its position to (x: 285, y: 120).
3. ButtonLoad
Select your ButtonLoad node and change its text to “Load Game”. Its font should be “QuinqueFive”, and the size of the font should be 30. Change the font color to #d98c8a. Also change its size to (x: 500, y: 100) and its position to (x: 285, y: 240).
4. ButtonQuit
Select your ButtonQuit node and change its text to “Quit”. Its font should be “QuinqueFive”, and the size of the font should be 30. Change the font color to #d98c8a. Also change its size to (x: 500, y: 100) and its position to (x: 285, y: 360).
You can change your AnimatedKing and AnimatedPig animations to be any animation you want. I duplicated my AnimatedPig node to be another pig that stands on our boxed friend. Also, draw the tilemap so that it has a variety from the other screens!
STEP 3: PAUSE MENU FUNCTIONALITY
To have our pause menu show, we’ll first need to create a new input called ui_pause that will open up our menu if we press an input such as “ESC”.
In our input() function, let’s make our pause menu visible when the player presses our newly created input. We’ll also pause the game to stop our players and spawners from processing.
### Player.gd
#older code
#singular input captures
func _input(event):
#pause game
if event.is_action_pressed("ui_pause"):
#pause scene
get_tree().paused = true
#show menu
$PauseMenu.visible = true
Then, in our resume button’s _on_button_resume_pressed() function, we’ll unpause our game and hide our menu.
### Player.gd
#older code
#resume game
func _on_button_resume_pressed():
#unpause scene
get_tree().paused = false
#hide menu
$PauseMenu.visible = false
Next, we’ll change our scene back to our MainMenu scene if the player presses our quit button. We’ll do these changes in our _on_button_quit_pressed() function.
### Player.gd
#older code
#quit to main menu
func _on_button_quit_pressed():
get_tree().change_scene_to_file("res://Scenes/MainMenu.tscn")
We’ll add the remaining functionality to our _load_pressed() and _save_pressed() in the next part when we add a save/load system.
Now if you run your scene, you should be able to pause your game, resume it, as well as quit the MainMenu scene.
STEP 4: MAIN MENU FUNCTIONALITY
To quit our game in our _on_button_quit_pressed() function, we can simply call the quit() method, which will kill and close the application entirely.
### MainMenu.gd
#older code
#quit game
func _on_button_quit_pressed():
get_tree().quit()
Now, in our _on_button_new_pressed() function, we will first need to clear our current_scene that our player is in. We need to do this to ensure that our current_scene is properly removed from the application’s memory, and thus we can assign a new current_scene which will override the data from the previous current_scene value.
### MainMenu.gd
#older code
#starts a new game in the Main scene, which is our 1st level
func _on_button_new_pressed():
# Get the current scene
var current_scene = get_tree().current_scene
# Free the current scene if it exists
if current_scene:
current_scene.queue_free()
After we’ve removed our current_scene from our memory, we need to create a new instance of our first level — which is our Main.tscn scene.
### MainMenu.gd
#older code
#starts a new game in the Main scene, which is our 1st level
func _on_button_new_pressed():
# Get the current scene
var current_scene = get_tree().current_scene
# Free the current scene if it exists
if current_scene:
current_scene.queue_free()
# Load the new scene
var new_scene = load("res://Scenes/Main.tscn").instantiate()
We then need to add this instanced scene as a child to our new scene, because we want this child to then be the new value of our current_scene variable.
### MainMenu.gd
#older code
#starts a new game in the Main scene, which is our 1st level
func _on_button_new_pressed():
# Get the current scene
var current_scene = get_tree().current_scene
# Free the current scene if it exists
if current_scene:
current_scene.queue_free()
# Load the new scene
var new_scene = load("res://Scenes/Main.tscn").instantiate()
# Add the new scene as a child of the root node
get_tree().root.add_child(new_scene)
# Set the new scene as the current scene
get_tree().set_current_scene(new_scene)
# Update the global variable with the name of the new scene
Global.current_scene_name = new_scene.name
Finally, we need to unpause the game to ensure that our new game does not start in a paused state.
### MainMenu.gd
#older code
#starts a new game in the Main scene, which is our 1st level
func _on_button_new_pressed():
# Get the current scene
var current_scene = get_tree().current_scene
# Free the current scene if it exists
if current_scene:
current_scene.queue_free()
# Load the new scene
var new_scene = load("res://Scenes/Main.tscn").instantiate()
# Add the new scene as a child of the root node
get_tree().root.add_child(new_scene)
# Set the new scene as the current scene
get_tree().set_current_scene(new_scene)
# Update the global variable with the name of the new scene
Global.current_scene_name = new_scene.name
# Ensures scene isn't paused
get_tree().paused = false
Your code should look like this.
Now if you run your Main scene, you should be able to start a new game as well as quit your game entirely.
With our main and pause menu’s bases set up, we can continue on to the next part where we will add the functionality to our player to save and load their game — which will load them into their last saved level.
Now would be a good time to save your project and make a backup of your project so that you can revert to this part if any game-breaking errors occur. Go back and revise what you’ve learned before you continue with the series, and once you’re ready, I’ll see you in the next part!
Next Part to the Tutorial Series
The tutorial series has 24 chapters. I’ll be posting all of the chapters in sectional daily parts over the next couple of weeks. You can find the updated list of the tutorial links for all 24 parts of this series on my GitBook. If you don’t see a link added to a part yet, then that means that it hasn’t been posted yet. Also, if there are any future updates to the series, my GitBook would be the place where you can keep up-to-date with everything!
Support the Series & Gain Early Access!
If you like this series and would like to support me, you could donate any amount to my KoFi shop or you could purchase the offline PDF that has the entire series in one on-the-go booklet!
The booklet gives you lifelong access to the full, offline version of the “Learn Godot 4 by Making a 2D Platformer” PDF booklet. This is a 451-page document that contains all the tutorials of this series in a sequenced format, plus you get dedicated help from me if you ever get stuck or need advice. This means you don’t have to wait for me to release the next part of the tutorial series on Dev.to or Medium. You can just move on and continue the tutorial at your own pace — anytime and anywhere!
This book will be updated continuously to fix newly discovered bugs, or to fix compatibility issues with newer versions of Godot 4.