All lessons
Lesson 26

Pit Runner

Cross the pits, grab every coin, and make three lives last. Assembles Platformer Movement, Collect Coins, and Lives System.

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

Platformer only

This is a project, not a lesson: nothing in this folder is new. Cross the pits, collect every coin, and make three lives last the whole way.

BUILD IT FROM

  1. Platformer Movement: the running, jumping player.
  2. Collect Coins and Items: the coins, emitting on the bus.
  3. Lives System: the pits, the respawn, and the counting.

WHAT THE GLUE DOES

The signal chain from the Lives System lesson runs unchanged: kill zone emits life_lost, this file subtracts and emits lives_updated, the HUD redraws. All this file adds is a second way for the round to end, collecting every coin, and a flag so the two endings cannot overlap.

The scenes

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

pit_runner.tscn
  • PitRunner Node2D pit_runner.gd
  • RunnerPlayer CharacterBody2D player.tscn
  • HUD CanvasLayer hud.tscn
  • Ground Node2D
  • Ground1 StaticBody2D
  • ColorRect
  • CollisionShape2D
  • Ground2 StaticBody2D
  • ColorRect
  • CollisionShape2D
  • Ground3 StaticBody2D
  • ColorRect
  • CollisionShape2D
  • Platform1 StaticBody2D
  • ColorRect
  • CollisionShape2D
  • Platform2 StaticBody2D
  • ColorRect
  • CollisionShape2D
  • Platform3 StaticBody2D
  • ColorRect
  • CollisionShape2D
  • Coins Node2D
  • Coin1 Area2D coin.tscn
  • Coin2 Area2D coin.tscn
  • Coin3 Area2D coin.tscn
  • Coin4 Area2D coin.tscn
  • Coin5 Area2D coin.tscn
  • Coin6 Area2D coin.tscn
  • KillZone Area2D kill_zone.gd
  • CollisionShape2D
coin.tscn
  • Coin Area2D coin.gd
  • ColorRect
  • CollisionShape2D
hud.tscn
  • HUD CanvasLayer hud.gd
  • MarginContainer
  • HBoxContainer
  • LivesLabel Label %
  • ScoreLabel Label %
  • ResultLabel Label %
  • HomeButton Button %
player.tscn
  • RunnerPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D
extends Node2D

## How many lives the player begins with.
@export var starting_lives: int = 3
## How many coins are in the scene. Change this if you add coins!
@export var total_coins: int = 6

var lives: int
var collected: int = 0
var round_over: bool = false

@onready var player: RunnerPlayer = $RunnerPlayer

func _ready() -> void:
	lives = starting_lives
	# kill_zone.gd emits "life_lost", the exact chain from Lives System.
	GameEvents.life_lost.connect(_on_life_lost)
	GameEvents.coin_collected.connect(_on_coin_collected)

func _on_life_lost() -> void:
	if round_over:
		return
	lives -= 1
	# hud.gd connects to "lives_updated" and redraws the number.
	GameEvents.lives_updated.emit(lives)
	if lives <= 0:
		round_over = true
		player.set_physics_process(false)
		# hud.gd shows Game Over.
		GameEvents.player_died.emit()
		_restart_soon()

func _on_coin_collected(_value: int) -> void:
	if round_over:
		return
	collected += 1
	if collected >= total_coins:
		round_over = true
		player.set_physics_process(false)
		# hud.gd shows the win message.
		GameEvents.goal_reached.emit()
		_restart_soon()

func _restart_soon() -> void:
	var timer: SceneTreeTimer = get_tree().create_timer(2.0)
	timer.timeout.connect(_on_restart_timeout)

func _on_restart_timeout() -> void:
	get_tree().reload_current_scene()