Learning Goals 3 min
By the end of this lesson you will be able to:
- Wire a four-state machine (Title → Play → Paused → GameOver) using a state variable and one state-gated forever per sprite.
- Use a when [space v] key pressed hat to leave the title screen and a when [p v] key pressed hat to toggle pause without breaking the falling-clone logic.
- Track lives, score, and a persistent high score so a missed catch ends the game and a new record sticks for next time.
Warm-Up — predict the pause bug 7 min
Across Cluster E you built each piece in isolation: a title screen (L03-26), backdrops as states (L03-27), a pause toggle (L03-28), a lives counter (L03-29), and a score with high-score (L03-30). Today you fuse all five into one playable game. But first — a prediction.
Aisyah copies her Lesson 28 pause toggle straight into a catch game. The falling apples are create clone of [Apple v], and each clone runs:
when I start as a clone
forever
change y by (-5)
if <touching [Catcher v] ?> then
delete this clone
end
end
She adds a separate pause script on the Stage that swaps the backdrop to Paused when P is pressed. She hits P mid-game. What actually happens to the apples?
Reveal the answer
The backdrop changes to Paused — but the apples keep falling. The catcher can still catch them. The game looks paused but isn't. Swapping a backdrop is just painting a new picture; it doesn't stop any scripts.
A real pause needs every moving sprite to ask "are we paused?" before it moves. Today's project gates every motion script behind a state variable, so when state = Paused, nothing falls.
Same trap will bite your title screen and your game-over screen. The fix is one shared variable that every script obeys.
New Concept — one variable, four behaviours 15 min
A state machine is a fancy name for a simple idea: your project is always in exactly one named situation, and every script knows which one. Today's machine has four states:
Title— the game hasn't started. Show a title backdrop. Wait for space.Play— the real game. Apples fall, catcher moves, score counts.Paused— frozen. Backdrop says PAUSED. Press P again to resume.GameOver— lives hit zero. Show final score and high score. Wait for space to restart.
The state variable
Make one state variable, For all sprites. Every sprite reads it; only the Stage (and the pause hat) writes to it.
when flag clicked
set [state v] to [Title]
switch backdrop to [Title v]
Gated forever loops
Every motion script you'd normally write as forever + action now becomes forever + if <> then + action. The hexagon checks the state:
when I start as a clone
forever
if <(state) = [Play]> then
change y by (-5)
if <touching [Catcher v] ?> then
change [score v] by (1)
delete this clone
end
end
end
State-changing hats
Backdrop swaps don't change state — key hats do. The pause hat toggles between Play and Paused:
when [p v] key pressed
if <(state) = [Play]> then
set [state v] to [Paused]
switch backdrop to [Paused v]
else
if <(state) = [Paused]> then
set [state v] to [Play]
switch backdrop to [Play v]
end
end
High score that survives
Scratch variables stay set between flag clicks as long as you don't reset them. So high score is set once at the start using if <> then only if score beats it — never blanket-reset on green flag.
Project — Build the full catch game 22 min
You're going to build a working game in ten steps. The cat as the catcher, a round kuih sprite as the falling clone, and four backdrops.
y = -150 and moves left/right only when state = Play. Kuih clones spawn at y = 170 and fall to y = -170; catch earns +1 score, miss costs a life. The state watcher is visible for testing — untick it before sharing.Step 1 — Backdrops and variables
New project. Paint four backdrops named exactly Title, Play, Paused, GameOver. Make three variables, all For all sprites: state, score, lives. Add a fourth: high score.
Step 2 — Stage: green-flag setup
On the Stage, set the world to Title:
when flag clicked
set [state v] to [Title]
switch backdrop to [Title v]
Note: no reset of high score. We want it to survive.
Step 3 — Stage: space starts the game
when [space v] key pressed
if <(state) = [Title]> then
set [score v] to (0)
set [lives v] to (3)
set [state v] to [Play]
switch backdrop to [Play v]
end
if <(state) = [GameOver]> then
set [score v] to (0)
set [lives v] to (3)
set [state v] to [Play]
switch backdrop to [Play v]
end
Step 4 — Stage: P toggles pause
when [p v] key pressed
if <(state) = [Play]> then
set [state v] to [Paused]
switch backdrop to [Paused v]
else
if <(state) = [Paused]> then
set [state v] to [Play]
switch backdrop to [Play v]
end
end
Step 5 — Stage: spawn apples (gated)
when flag clicked
forever
if <(state) = [Play]> then
create clone of [Apple v]
wait (pick random (0.6) to (1.4)) seconds
end
end
Step 6 — Catcher: arrow keys (gated)
On the Catcher sprite. Place at x: 0, y: -150.
when flag clicked
go to x: (0) y: (-150)
forever
if <(state) = [Play]> then
if <key [left arrow v] pressed?> then
change x by (-8)
end
if <key [right arrow v] pressed?> then
change x by (8)
end
end
end
Step 7 — Apple: clone behaviour
On the Apple sprite. Hide the original. Each clone spawns at the top:
when I start as a clone
show
go to x: (pick random (-220) to (220)) y: (170)
forever
if <(state) = [Play]> then
change y by (-5)
if <touching [Catcher v] ?> then
change [score v] by (1)
delete this clone
end
if <(y position) < (-170)> then
change [lives v] by (-1)
delete this clone
end
end
end
Step 8 — Lives-watcher (Stage)
The Stage watches for game-over. Update high score only if beaten:
when flag clicked
forever
if <(lives) = (0)> then
if <(state) = [Play]> then
if <(score) > (high score)> then
set [high score v] to (score)
end
set [state v] to [GameOver]
switch backdrop to [GameOver v]
end
end
end
Step 9 — Scoreboard sprite on GameOver
Add a Scoreboard sprite (or use the Catcher with a costume switch). It greets the player on the GameOver screen:
when backdrop switches to [GameOver v]
go to x: (0) y: (0)
show
say (join [Final: ] (join (score) (join [ · Best: ] (high score)))) for (4) seconds
hide
Step 10 — Test the full flow
Click the flag. You should see the Title backdrop. Press space — falling apples begin. Catch some. Press P mid-fall — everything freezes. Press P again — it resumes. Miss three apples — Game Over backdrop appears with your final and best. Press space — fresh game, but high score sticks.
The full Stage script bundle
when flag clicked
set [state v] to [Title]
switch backdrop to [Title v]
when [space v] key pressed
if <(state) = [Title]> then
set [score v] to (0)
set [lives v] to (3)
set [state v] to [Play]
switch backdrop to [Play v]
end
when [p v] key pressed
if <(state) = [Play]> then
set [state v] to [Paused]
switch backdrop to [Paused v]
end
What you just built: a complete arcade-style game with a real menu, real pause, real death, and a record that survives. Same skeleton as Flappy Bird, Doodle Jump, or any Roblox obby — a state variable plus gated loops.
Try It Yourself — three game-state drills 10 min
Goal: Add a sound cue. Whenever an apple is caught, play play sound [Pop v] until done. Make sure the cue only plays during Play — not during paused (it shouldn't be possible to catch one then, but be defensive).
if <touching [Catcher v] ?> then
if <(state) = [Play]> then
play sound [Pop v] until done
change [score v] by (1)
delete this clone
end
end
Think: The outer gate already protects you, but layering a second check is a habit good game makers build early. Two locks beat one.
Goal: Speed up the falling apples after every 5 points. Use a fall speed variable (start at 5). Whenever score hits a multiple of 5, add 1 to fall speed. Use it in the clone's change y by ((0) - (fall speed)).
when flag clicked
set [fall speed v] to (5)
forever
if <((score) mod (5)) = (0)> then
if <(score) > (0)> then
set [fall speed v] to ((5) + ((score) / (5)))
end
end
end
Think: You met () mod () back in L03-04. The "is this score a multiple of 5?" question is one of its classic uses.
Goal: Add a fifth state, Win. Reach 20 points and the game ends in victory instead of waiting for lives to hit zero. Paint a Win backdrop. Add a fifth check inside the lives-watcher.
forever
if <(score) = (20)> then
if <(state) = [Play]> then
if <(score) > (high score)> then
set [high score v] to (score)
end
set [state v] to [Win]
switch backdrop to [Win v]
end
end
end
Think: Adding a state is now a small change, not a rewrite. That's the whole reward of the state-machine pattern — you ship the original game, then add modes for the rest of your life.
Mini-Challenge — the pause that wasn't 5 min
"Iqbal's frozen catcher"
Iqbal builds the game but forgets one gate. He notices that when he presses P, the apples freeze (good!), but his crab catcher still slides left and right. He shows you his catcher script:
when flag clicked
go to x: (0) y: (-150)
forever
if <key [left arrow v] pressed?> then
change x by (-8)
end
if <key [right arrow v] pressed?> then
change x by (8)
end
end
What does the catcher need so it freezes when the world freezes?
Reveal one valid solution
Every motion forever needs the state gate, including the catcher's. Wrap the two arrow-key checks in if <(state) = [Play]> then:
when flag clicked
go to x: (0) y: (-150)
forever
if <(state) = [Play]> then
if <key [left arrow v] pressed?> then
change x by (-8)
end
if <key [right arrow v] pressed?> then
change x by (8)
end
end
end
The rule of state machines: any forever that does anything visible needs to ask the state question first. Hats that change state (key presses, flag clicks) are the exception — they're meant to fire any time.
Recap 3 min
You wired five separate Cluster E ideas — title screen, backdrop states, pause toggle, lives, high score — into one polished Scratch project: the Pause-able Catch Game. The pattern is one shared state variable, every forever gated by an if-then that reads it, and only a handful of hats (flag, space, P, lives-watcher) that write to it. That's the same pattern AAA games use; you just built a smaller one.
- State machine
- A project where one variable names the current situation (Title, Play, Paused, GameOver) and every script behaves differently depending on its value.
- Gated loop
- A forever wrapped around an if <> then that only runs its body when a condition (usually state) is true. The loop never stops, but the action does.
- State transition
- A moment where the state variable changes — almost always inside a hat (key, flag, broadcast) or a watcher script (lives = 0).
- Persistent variable
- A variable that intentionally isn't reset on green flag, so its value survives across plays. high score is the classic example.
- Watcher script
- A small forever loop that watches a condition (lives = 0, score > high score) and triggers a state change when it fires. The catch game has two: lives-watcher and high-score check.
Homework 2 min
Make it yours. Take today's project and personalise it — but keep the four-state skeleton intact.
- Repaint the four backdrops. Title should say your game's name (e.g. Kucing Tangkap Buah!). GameOver should show your initials.
- Replace the apple with a different falling sprite (donut, durian, RM50 note — your call). Replace the catcher too.
- Add one new state-related hat: pressing R on the GameOver screen restarts immediately without waiting for space. Use when [r v] key pressed + the same restart logic from step 3 in the worked example.
- Save as
HW-L3-31-CatchGame-<yourname>.sb3.
Bring back next class:
- Your
.sb3file with all four backdrops working. - A one-line answer to: "Why doesn't pressing P during the Title screen do anything? Trace the toggle script and explain."
- Your highest score (the variable should still hold it when class starts).
Heads up for next class: SCR-L03-32 opens Cluster F — top-down mazes. You'll spend it inside the backdrop editor with the line tool, not the script area. Bring your imagination, not your keyboard fingers.