Lesson 12

Win and Lose Screen

Two zones, two endings, one full-screen panel. Uses a flag so the game can only end once.

Loading the game engine. This takes a moment the first time.

Two zones. Green wins, red loses. Either one ends the game and brings up the full screen panel.

New code in this lesson

None of the earlier lessons use these. Each one shows the line where it appears here.

show() and hide()

Makes a node appear or disappear, along with everything under it.

game_over_panel.gd
overlay.hide()

Color()

A color made of red, green and blue, each from 0 to 1. A fourth number sets how see-through it is.

game_over_panel.gd
_end_game("You Win!", Color(0.4, 1.0, 0.5))

modulate

Tints a node and everything under it. Its fourth number fades it out.

game_over_panel.gd
message_label.modulate = color

get_tree().paused

Set it to true and the whole game freezes, apart from nodes told to keep running.

game_over_panel.gd
get_tree().paused = true

grab_focus()

Selects a button, so Enter or Space presses it straight away.

game_over_panel.gd
play_again_button.grab_focus()

reload_current_scene()

Starts the current scene over from the beginning.

game_over_panel.gd
get_tree().reload_current_scene()

The scenes

■ UI node · ■ 2D node · % unique name · .tscn instanced scene

game.tscn
  • Game Node2D game.gd
  • Player CharacterBody2D player.tscn
  • WinZone Area2D %
  • Polygon2D
  • CollisionShape2D
  • LoseZone Area2D %
  • Polygon2D
  • CollisionShape2D
  • GameOverPanel CanvasLayer game_over_panel.tscn
game_over_panel.tscn
  • GameOverPanel CanvasLayer game_over_panel.gd
  • Overlay Control hidden %
  • Dim ColorRect
  • CenterContainer
  • VBoxContainer
  • MessageLabel Label %
  • PlayAgainButton Button %
  • HomeButton Button %
player.tscn
  • Player CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D

FileSystem

  • game.tscn
  • game.gd
  • game_over_panel.tscn
  • game_over_panel.gd
  • player.tscn
  • player.gd

The code

extends Node2D

# "$GameOverPanel" is the node named GameOverPanel, sitting under this one.
@onready var game_over_panel: GameOverPanel = $GameOverPanel

## Once the game is over, ignore any more zone touches.
var is_game_over: bool = false

@onready var win_zone: Area2D = %WinZone
@onready var lose_zone: Area2D = %LoseZone

func _ready() -> void:
	win_zone.body_entered.connect(_on_win_zone_entered)
	lose_zone.body_entered.connect(_on_lose_zone_entered)

func _on_win_zone_entered(body: Node2D) -> void:
	# Two reasons to quit early: the game already ended, or the thing that
	# touched the zone was not the player. "not (...)" flips true and false.
	if is_game_over or not (body is Player):
		return
	# Set the flag FIRST, so a second touch cannot get past the line above.
	is_game_over = true
	# GameOverPanel is our child, so we skip signals and call its function.
	game_over_panel.show_win()

func _on_lose_zone_entered(body: Node2D) -> void:
	if is_game_over or not (body is Player):
		return
	is_game_over = true
	game_over_panel.show_lose()