Learning Goals 3 min
By the end of this lesson you will be able to:
- Assemble a complete top-down maze game from the parts you built across Cluster F — walls, arrow-key player, patrolling enemies, collectibles, and a goal — into one playable project.
- Use a single game-state variable to switch between title, play, win, and gameover screens, so each sprite knows which behaviour to run.
- Wire up 3 keys, 1 goal, 2 enemies, and 3 lives with broadcasts so collecting, dying, and winning all stay in sync across sprites.
Warm-Up — predict the loop 7 min
Across Cluster F you built each piece on its own. The player moves with arrow keys. Walls block. Enemies patrol. Keys vanish when touched. Today we glue them all together — but first, a question about the order things happen in.
when flag clicked
forever
if <key [right arrow v] pressed?> then
change x by (4)
end
if <touching color [#3b2a8a] ?> then
change x by (-4)
end
end
Imagine the player presses right and runs into a wall. What does the screen look like — does the player slide into the wall, bounce off it, or freeze right at its edge?
Reveal the answer
The player freezes right at the edge of the wall, with no visible jitter. The forever loop runs many times a second. In one tick, change x by (4) nudges the player into the wall, then the very next block — if <touching color [#3b2a8a] ?> then — fires and change x by (-4) shoves the player back out. The eye never sees the "in the wall" frame. This move-then-check-then-undo pattern is how every wall-collision in your maze is going to work.
Today you'll see the exact same pattern in four directions, then bolt on enemies, keys, a goal, lives, and a tiny title-screen state machine. By the end of the period you'll click the flag and play your own maze.
New Concept — gluing a whole game together 15 min
A finished game is not one big script. It's a handful of small scripts on different sprites that agree with each other through variables and broadcasts. Today we meet the three glue ideas that hold a maze game together.
The game-state variable
One "for all sprites" variable, game-state, holds one of four words: title, play, win, or gameover. Every sprite checks this before doing anything. Enemies don't patrol on the title screen. The player doesn't respond to keys on the gameover screen. One variable, one source of truth.
when flag clicked
set [game-state v] to [title]
forever
if <key [space v] pressed?> then
if <(game-state) = [title]> then
set [game-state v] to [play]
end
end
end
Lives and the keys-collected counter
Two more "for all sprites" variables do the bookkeeping. lives starts at 3. keys-left starts at 3. Touch an enemy → change [lives v] by (-1). Touch a key → change [keys-left v] by (-1) and the key sprite hides itself. When lives hits 0, switch game-state to gameover. When the player touches the goal and keys-left is 0, switch to win.
Broadcasts for "something happened"
When the player dies, the player sprite broadcast [respawn v]. Every sprite that needs to react — the player itself jumps to the start position; enemies could reset to their patrol points — listens with when I receive [respawn v]. Broadcasts are how sprites talk without polling.
when I receive [respawn v]
go to x: (-200) y: (-150)
point in direction (90)
The wall-collision four-step
From the warm-up. Each arrow key has the same shape: move, then check, then undo if you hit a wall. You write it four times — one per direction — and walls just work.
Worked Example — Build the full maze 12 min
Open Scratch. We're building the whole thing in ten steps. Save often.
Step 1 — The maze backdrop
Click the Stage, then Backdrops. Paint a new backdrop. Fill the background with a light tan. Use the rectangle tool with dark blue (#3b2a8a) to draw walls — the outer border plus inner walls that split the Stage into two rooms, with one narrow opening between them. Leave space for paths. This colour is your wall — every sprite will sense it.
Step 2 — Three variables and one list-free state
Variables panel → Make a Variable, "for all sprites": game-state, lives, keys-left. Tick the boxes for lives and keys-left so they show on the Stage; untick game-state.
Step 3 — The player sprite
Add a small round sprite (paint one or pick a hero). Name it Player. Its main script handles arrow keys and wall collisions:
when flag clicked
go to x: (-200) y: (-150)
forever
if <(game-state) = [play]> then
if <key [right arrow v] pressed?> then
change x by (4)
if <touching color [#3b2a8a] ?> then
change x by (-4)
end
end
if <key [left arrow v] pressed?> then
change x by (-4)
if <touching color [#3b2a8a] ?> then
change x by (4)
end
end
if <key [up arrow v] pressed?> then
change y by (4)
if <touching color [#3b2a8a] ?> then
change y by (-4)
end
end
if <key [down arrow v] pressed?> then
change y by (-4)
if <touching color [#3b2a8a] ?> then
change y by (4)
end
end
end
end
Step 4 — Three key sprites
Paint a small yellow key. Name it Key1. Duplicate it twice → Key2, Key3. Place each in a different corner of the maze. On each key sprite:
when I receive [start v]
show
forever
if <touching [Player v] ?> then
change [keys-left v] by (-1)
hide
stop [this script v]
end
end
Step 5 — Two patrolling enemies
Paint a red square. Name it Enemy1. Duplicate → Enemy2. Each patrols between two points using a simple back-and-forth. On Enemy1:
when I receive [start v]
go to x: (-50) y: (50)
point in direction (90)
forever
if <(game-state) = [play]> then
move (2) steps
if on edge, bounce
if <touching color [#3b2a8a] ?> then
turn cw (180) degrees
move (4) steps
end
if <touching [Player v] ?> then
broadcast [hit v]
wait (1) seconds
end
end
end
Give Enemy2 a different start position and a different starting direction so they patrol different corridors.
Step 6 — The green goal sprite
Paint a green square. Name it Goal. Place it in the far corner. The goal only matters when all three keys are collected:
when I receive [start v]
show
forever
if <<touching [Player v] ?> and <(keys-left) = (0)>> then
set [game-state v] to [win]
end
end
Step 7 — The hit broadcast and lives
The enemy broadcasts hit. The player handles it: lose a life, jump home, and if lives hit 0, switch to gameover.
when I receive [hit v]
change [lives v] by (-1)
go to x: (-200) y: (-150)
if <(lives) = (0)> then
set [game-state v] to [gameover]
end
Step 8 — The Game-Controller sprite
Add a new sprite (you can use a small flag icon or a hidden dot). Name it Controller. This sprite owns the state machine and the backdrop messaging:
when flag clicked
set [game-state v] to [title]
set [lives v] to (3)
set [keys-left v] to (3)
switch backdrop to [title v]
forever
if <<(game-state) = [title]> and <key [space v] pressed?>> then
switch backdrop to [maze v]
broadcast [start v]
set [game-state v] to [play]
end
if <(game-state) = [win]> then
switch backdrop to [win v]
end
if <(game-state) = [gameover]> then
switch backdrop to [gameover v]
end
end
Step 9 — Title, win, and gameover backdrops
Paint three more backdrops: a title screen ("Press SPACE to start"), a win screen ("You escaped!"), a gameover screen ("Out of lives — press the flag"). The Controller switches between them based on game-state.
Step 10 — Click the flag and play
Title screen appears. Press space. The maze loads. Walk with arrows. Touch the three yellow keys — they vanish, keys-left ticks down. Avoid the red squares — touch one and you lose a life, snap back to the start. Reach the green goal with all keys collected → win screen. Lose all three lives → game over.
The full player script (assembled)
when flag clicked
go to x: (-200) y: (-150)
forever
if <(game-state) = [play]> then
if <key [right arrow v] pressed?> then
change x by (4)
if <touching color [#3b2a8a] ?> then
change x by (-4)
end
end
if <key [left arrow v] pressed?> then
change x by (-4)
if <touching color [#3b2a8a] ?> then
change x by (4)
end
end
end
end
What you just built: a complete playable game — title, levels, win, lose, lives, collectibles, enemies. Every Cluster F lesson contributed one piece. This is the longest project of Scratch so far, and it works because each sprite minds its own job.
Try It Yourself — three game-polish drills 15 min
Goal: Add a fourth life. Change the controller so lives starts at 4 instead of 3. Then change the gameover check on the player to fire at 0 (it already does). Test by deliberately dying four times.
when flag clicked
set [game-state v] to [title]
set [lives v] to (4)
set [keys-left v] to (3)
Think: Numbers like 3 and 4 are tuning knobs. The whole game balance hangs off them. Game designers spend hours twiddling exactly these.
Goal: Make Enemy1 faster — but only after one key has been collected. Inside Enemy1's forever loop, check keys-left and use 3 steps instead of 2 if fewer than 3 keys remain.
when I receive [start v]
forever
if <(game-state) = [play]> then
if <(keys-left) < (3)> then
move (3) steps
else
move (2) steps
end
if on edge, bounce
end
end
Think: The enemy is now reactive to game progress. As the player gets closer to winning, the game gets harder. That's a classic difficulty curve in two extra blocks.
Goal: Add a timer. Use a time-left variable that starts at 60 and ticks down by 1 every second while in play state. If it reaches 0, switch game-state to gameover. Show time-left on the Stage.
when I receive [start v]
set [time-left v] to (60)
forever
wait (1) seconds
if <(game-state) = [play]> then
change [time-left v] by (-1)
if <(time-left) = (0)> then
set [game-state v] to [gameover]
end
end
end
Think: The timer lives on the Controller. The player doesn't know about it. The enemies don't know about it. They all just watch game-state. That's the power of one shared variable.
Mini-Challenge — Hafiz's win-without-keys bug 5 min
"I walked onto the goal and won, but I never picked up the keys"
Hafiz built the maze. He shows it to his sister. She walks her player straight to the green goal, ignoring the yellow keys entirely — and the win screen appears. Hafiz checks his Goal sprite:
when I receive [start v]
show
forever
if <touching [Player v] ?> then
set [game-state v] to [win]
end
end
What's missing, and what should the if-condition really say?
Reveal one valid solution
The condition only checks "touching the player". It never checks that the keys have been collected. Hafiz needs to combine two questions with <> and <>: touching the player and keys-left equals zero.
when I receive [start v]
show
forever
if <<touching [Player v] ?> and <(keys-left) = (0)>> then
set [game-state v] to [win]
end
end
Now stepping on the goal does nothing until keys-left is 0 — which only happens once all three keys have been touched. Win conditions almost always combine multiple checks. An if-then with one question is fine for movement; an if-then for "did the player win" usually needs two or three.
Recap 3 min
You built your first full game — title screen, playable maze, win screen, gameover screen. The whole thing is held together by three "for all sprites" variables (game-state, lives, keys-left) and two broadcasts (start and hit). Every sprite checks game-state before acting, so the world only "lives" during play. Walls work because every direction move is followed by a check-and-undo. Keys vanish on touch by stopping their own forever loop. The goal only counts when keys-left hits 0. This is the same architecture used in every Scratch arcade-game tutorial on the internet — you can now read those tutorials and recognise what they're doing.
- State machine
- A pattern where one variable holds the current "mode" of the game (
title,play,win,gameover) and every script branches on that variable. The cleanest way to manage multiple screens in one project. - Wall collision
- The "move, check, undo" pattern: move the player a few pixels, then if they're touching a wall colour, move them back the same amount. Done per direction.
- Broadcast
- A message any sprite can send (broadcast [start v]) and any sprite can listen for (when I receive [start v]). Used to keep sprites in sync without polling.
- Patrol
- An enemy's back-and-forth movement, usually move + if on edge, bounce or a wall-bounce. The enemy doesn't chase — it just walks a fixed path.
- Win condition
- The combined check that decides the player has finished. In this game: touching [Player v]? and <(keys-left) = (0)>.
Homework 2 min
Pasar Malam Maze. Reskin today's maze game with a Malaysian night-market theme. Same mechanics, new look.
- Repaint the backdrop. Walls become market stalls (still in one consistent wall colour). Two rooms become two sections of the pasar.
- The player becomes a tudung-wearing customer (paint or pick a sprite). Place at the entrance.
- The three yellow keys become ringgit notes (RM5, RM10, RM20). Same collect-and-vanish behaviour.
- The two red enemies become angry stray cats. Same patrol behaviour.
- The green goal becomes a satay stall. You can only "buy" satay when all three notes are collected.
- Keep lives at 3. Win = "Satay confirmed!". Gameover = "Cats won. Try again."
Save as HW-L3-37-Pasar-Malam-Maze.sb3.
Bring back next class:
- The
.sb3file. - Your answer to: "What's the one thing in your maze you'd change to make it harder? Why? (Could be: more cats, faster cats, fewer lives, smaller corridors, a timer, an extra RM note to find.)"
Heads up for next class: SCR-L03-38 opens Cluster G — Scrolling Platformers. The trick: instead of moving the player across the Stage, you keep the player fixed in the centre and move the world past them. It feels like a completely different kind of game, but it's built from the same blocks you already know.