One thing you’ll find in almost all games is the pause menu, which usually has a number of controls such as “resume game”, “restart level”, “return to main menu”, “game settings”, etc. In this tutorial, we’ll learn how to create and script a pause menu for your game.
The pause menu we’ll make for our game will contain these 3 menu items:
- Resume game
- Restart game
The player will open the menu by pressing the escape key, then he can navigate it with the arrow keys and select the highlighted item by pressing the space bar.
Drawing the menu GUI
The final result we want to achieve is this:
Let’s see how to do it!
Add a Popup node to CanvasLayer an call it MenuPopup. Make it visibile by clicking on the eye icon next to the node name.
Since the menu must work when the game is paused, set MenuPopup’s Pause → Mode property to Process. Also, set Exclusive to On to prevent the popup to close in response to mouse events.
Add a ColorRect node to MenuPopup. Set Rect → Position to (60, 40) and Rect → Size to (200, 100), then set the Color property to (80,80,80,255).
Add another ColorRect to MenuPopup and rename it Resume. Set Rect → Position to (70, 50) and Rect → Size to (180, 20). Now add a Label to Resume. In the Custom Font property load the font Font.tres (you’ll find it in the Fonts folder) and in Custom Colors set Font Color to black. Write RESUME GAME in the Text property of the label, then set both Align and Valign to Center. Finally, set Rect → Size to (180, 20).
Now, duplicate Resume two times. Rename one copy to Restart and the other to Quit. You’ll end up with this node structure for MenuPopup:
Set Restart‘s Rect → Position to (70, 80), then change its label’s text to RESTART GAME. Select the Quit node and set its Rect → Position to (70, 110). Finally, change its label’s text to QUIT.
Attach a new script to MenuPopup and save it as MenuPopup.gd in the GUI folder.
First, let’s add some variables:
onready var player = get_node("/root/Root/Player") var already_paused var selected_menu
The player variable will contain a reference to the Player node. To initialize it, we used a new keyword: onready. The onready keyword defers initialization of a variable until _ready() is called. That single line is a compact way to write the following code:
var player func _ready(): player = get_node("/root/Root/Player")
The already_paused variable is used to store if the game was already paused when the menu was called. If so, we don’t have to un-pause the game when leaving the menu (for example, the game is already paused if we open the menu during a conversation with an NPC).
The last variable, selected_menu, stores the currently highlighted menu item.
Before writing the code that handles the menu input, let’s write a function that set the color of the menu items, in order to highlight the current one:
func change_menu_color(): $Resume.color = Color.gray $Restart.color = Color.gray $Quit.color = Color.gray match selected_menu: 0: $Resume.color = Color.greenyellow 1: $Restart.color = Color.greenyellow 2: $Quit.color = Color.greenyellow
The first three lines set the menu items to the default color. Then, the match statement is used to check which item is highlighted to change its color.
Now we can start writing the basic structure of the _input () function:
func _input(event): if event is InputEventKey: var just_pressed = event.pressed and not event.echo if not visible: if event.scancode == KEY_ESCAPE and just_pressed: pass #TODO else: if event.scancode == KEY_DOWN and just_pressed: pass #TODO elif event.scancode == KEY_UP and just_pressed: pass #TODO elif event.scancode == KEY_SPACE and just_pressed: match selected_menu: 0: pass #TODO: resume game 1: pass #TODO: restart game 2: pass #TODO: quit
First, the function checks if the input is a keyboard event. If so, the variable just_pressed is declared. This variable will contain the value true if the key has just been pressed, or false otherwise. When handling input, we’ll check this value to prevent the current menu item from continuosly change when we hold down the arrow keys.
How do you know if the key has just been pressed? Two conditions must occur simultaneously:
- the key must be pressed: checking if pressed is true
- the key should not have been pressed before this event: checking if echo is false
At this point we have to handle two possible states:
- the menu is hidden: if the player presses the ESC key, we want to pause the game and show the menu.
- the menu is visible: we want to navigate the menu with the up and down arrow keys and select the highlighted item with the space bar.
Let’s start from the first state. Add this code for when the menu is hidden and the player presses the escape key (KEY_ESCAPE):
# Pause game get_tree().paused = true # Reset the popup selected_menu = 0 change_menu_color() # Show popup player.set_process_input(false) popup()
This code does the following:
- pause the game setting the paused property of the game tree to true
- reset the menu by choosing the first item and calling the change_menu_color() function
- disables the input processing for Player
- finally, shows the menu calling the popup() function.
When the menu is visible, we have to handle the arrow keys. For the down arrow key (KEY_DOWN), enter this code:
selected_menu = (selected_menu + 1) % 3; change_menu_color()
To select the next menu item, you need to increase the value of the selected_menu variable. This works until the selected item is the last one: in that case, selected_menu is 2 and increasing it would go to item 3, that does not exist. If you want a menu where going forward from the last menu item bring you back to the first one, a compact way to do it is to use the modulo (%) operation.
Once the selected menu has changed, the function calls change_menu_color() to update the color of the menu items.
The code for the up arrow (KEY_UP) is similar, but in this case we cannot use the modulo operation. Instead, we will use the if statement to check what is item the current menu item:
if selected_menu > 0: selected_menu = selected_menu - 1 else: selected_menu = 2 change_menu_color()
When the space bar is pressed, we will execute the code for the highlighted menu item. For case 0 of the match statement (Resume Game), enter this code:
# Resume game if not already_paused: get_tree().paused = false player.set_process_input(true) hide()
To resume the game, this code set the pause property of the game tree to false (but only if the game was not already paused), reactivates the input processing for the Player node and hides the menu.
Restarting the game is simple: we must simply reload the main scene to bring everything back to the initial state of the game and then remove the pause. So, for case 1 of the match statement enter this code:
# Restart game get_tree().change_scene("res://Scenes/Main.tscn") get_tree().paused = false
Quitting the game is even simpler: just call the quit() function of the game tree. Add this code for the case 2 of the match statement:
# Quit game get_tree().quit()
For convenience, I report below the final code of the _input () function:
func _input(event): if event is InputEventKey: var just_pressed = event.pressed and not event.echo if not visible: if event.scancode == KEY_ESCAPE and just_pressed: # Pause game already_paused = get_tree().paused get_tree().paused = true # Reset the popup selected_menu = 0 change_menu_color() # Show popup player.set_process_input(false) popup() else: if event.scancode == KEY_DOWN and just_pressed: selected_menu = (selected_menu + 1) % 3; change_menu_color() elif event.scancode == KEY_UP and just_pressed: if selected_menu > 0: selected_menu = selected_menu - 1 else: selected_menu = 2 change_menu_color() elif event.scancode == KEY_SPACE and just_pressed: match selected_menu: 0: # Resume game if not already_paused: get_tree().paused = false player.set_process_input(true) hide() 1: # Restart game get_tree().change_scene("res://Scenes/Main.tscn") get_tree().paused = false 2: # Quit game get_tree().quit()
In this tutorial we have learned how to make a simple pause menu. As an exercise, you can try making a similar menu on the Game Over screen.
You can try the game with the new menu by clicking here (obviously Quit doesn’t work in the browser):
In the next tutorial we will learn how to export the game to create the executable and the HTML5 versions.