Three floor switches, and not one line of game_events.gd. Write a signal that lives inside its own node, and learn when a plain function call beats a signal.
Loading the game engine. This takes a moment the first time.
This lesson uses no game_events.gd at all, and that is the point of it. Every lesson since Platformer Movement has put its news on the shared bus. This level does the same kind of job without one, so you can see what the bus is FOR by watching a level that does not need it.
hud.set_count(2, 3), or a collision handed it to you, like body.take_damage(1).game_events.gd, the shared bus.hud.set_count(2, 3)_ready(), which only works because all three switches are already in the level when it starts. Nothing can connect to a node that does not exist yet.None of the earlier lessons use these. Each one shows the line where it appears here.
A loop that runs the lines under it once for every item in a list, or once for every number up to a count.
for child in get_children():A list of every node directly under this one.
for child in get_children():The fill color of a Polygon2D or ColorRect.
square.color = Color(0.2, 0.9, 0.3)■ UI node · ■ 2D node · % unique name · .tscn instanced scene
extends Node2D
# Both counted in "_ready()" below. Nobody types these by hand.
var total_switches: int = 0
var switches_on: int = 0
@onready var hud: SwitchHUD = %HUD
func _ready() -> void:
# Walk our own children and pick out the switches.
for child in get_children():
if child is FloorSwitch:
total_switches += 1
# "flipped" is a LOCAL signal, written inside switch.gd. We can
# connect to it because this switch is our own child, so we are
# holding it right here as "child".
child.flipped.connect(_on_switch_flipped)
hud.set_count(switches_on, total_switches)
# Runs when ANY one switch emits "flipped". All three connected to this same
# function, so one function covers however many switches the level has.
func _on_switch_flipped() -> void:
switches_on += 1
hud.set_count(switches_on, total_switches)
if switches_on >= total_switches:
hud.show_win()class_name SwitchHUD
extends CanvasLayer
@onready var score_label: Label = %ScoreLabel
@onready var home_button: Button = %HomeButton
func _ready() -> void:
# "pressed" is a signal Godot builds into every Button.
home_button.pressed.connect(_on_home_pressed)
# Loads the lesson list screen, which replaces this whole scene.
func _on_home_pressed() -> void:
get_tree().change_scene_to_file("res://shared/ui/lesson_select.tscn")
## Called BY game.gd with no signal at all.
func set_count(on: int, total: int) -> void:
# Two blank spots, so the numbers go in a list: [first, second]
score_label.text = "Switches: %d / %d" % [on, total]
## Also called BY game.gd, once the last switch is on.
func show_win() -> void:
score_label.text = "Gate open!"class_name Player
extends CharacterBody2D
## How fast the player moves.
@export var speed: float = 200.0
@onready var sprite: Sprite2D = $Sprite2D
func _physics_process(_delta: float) -> void:
var direction: Vector2 = Input.get_vector(
"move_left", "move_right", "move_up", "move_down"
)
velocity = direction * speed
move_and_slide()
# The warrior picture only faces right. Walking left mirrors it.
if direction.x != 0.0:
sprite.flip_h = direction.x < 0.0class_name FloorSwitch
extends Area2D
# "flipped" is a LOCAL signal. We wrote it right here, so it belongs to this
# one switch. It is NOT in game_events.gd and does not need to be: the level
# is our parent, so it can reach every switch and connect to each one.
signal flipped
# Stays true once stepped on, so walking back over it does not count twice.
var is_on: bool = false
@onready var square: Polygon2D = $Polygon2D
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
# Already on. Nothing to announce.
if is_on:
return
# "Player" is the class_name at the top of player.gd.
if body is Player:
is_on = true
square.color = Color(0.2, 0.9, 0.3)
# Send the news UP to whoever connected. We do not know or care who
# that is. Right now it is game.gd.
flipped.emit()