Learning Goals 3 min
By the end of this lesson you will be able to:
- Build a boss sprite with its own HP bar (a boss-hp variable) that takes multiple hits to defeat — instead of dying in one touch like a regular enemy.
- Drive the boss with a state machine: a boss-state variable that switches the sprite between a slow patrol and a fast attack jump.
- Detect a "stomp" hit — the boss only takes damage when the player touches it while falling onto it from above (player's velocity-y is positive in our up-is-positive coordinate system), and broadcast victory when HP reaches zero.
Warm-Up — the one-hit problem 7 min
Every enemy you've built in this cluster has had exactly the same logic: if I touch the player, the player dies (or I do). One touch, one outcome. Here's the typical patrol enemy from SCR-L04-19:
when flag clicked
forever
move (2) steps
if <touching (Player v) ?> then
broadcast (game-over v)
end
end
Now imagine the boss of your platformer working that way. Walk into the room → touch boss → game over. The "boss fight" lasts a quarter of a second. Predict: what two ideas would you have to add to turn this one-hit enemy into a proper boss battle?
Reveal the answer
You need HP (a hit-points number that goes down with each hit, not down to zero on the first touch) and you need a different hit rule for the player — otherwise the player still dies the moment they get near it, and HP is pointless. The classic platformer answer is: the boss kills the player on side-touches, but the player kills the boss by jumping on its head. That gives the player a fair way to win.
Both of those are today's lesson. You'll also add a third idea: a state machine so the boss does something more interesting than walk in a straight line.
Predict puzzle. Look at this fragment of boss code:
forever
if <(boss-state) = [patrol]> then
move (2) steps
end
if <(boss-state) = [attack]> then
change y by (10)
end
end
Reveal the answer
It depends on the value of boss-state. If the variable is patrol, the boss moves 2 steps every frame (a slow walk). If it's attack, the boss jumps up 10 pixels every frame (a fast leap). If it's neither word — say idle, or empty — the boss does nothing. A string variable used to choose between behaviours is called a state machine, and it's how every boss in every game decides what to do next.
New Concept — HP + state machine + stomp 15 min
A boss is just a bigger, smarter enemy. Three new ideas turn a regular enemy into one.
Idea 1 — HP (hit points)
Make a variable. Call it boss-hp. Set it to 5 at the start of the boss fight. Every time the player lands a hit, change [boss-hp v] by (-1). When it hits 0, the boss dies. That's it.
when I receive (start-boss-fight v)
set [boss-hp v] to (5)
show
Tick the checkbox next to boss-hp in the Variables palette so the number shows on the Stage. Now the player can see they're making progress.
Idea 2 — a state machine
A "state" is a word that describes what the sprite is doing right now. A state machine is one variable holding that word, plus a forever loop that does different things depending on the word.
forever
if <(boss-state) = [patrol]> then
move (2) steps
if <touching (_edge_ v) ?> then
turn cw (180) degrees
end
end
if <(boss-state) = [attack]> then
change y by (10)
end
end
Then a separate script flips the state every few seconds, so the boss alternates between walking and jumping:
when I receive (start-boss-fight v)
forever
set [boss-state v] to [patrol]
wait (3) seconds
set [boss-state v] to [attack]
wait (1) seconds
end
Idea 3 — the stomp hit
The boss is dangerous to touch from the side. But the player has to be able to win, so we give them one way to land a hit: jumping on the boss's head. In platformer terms, that means the player is touching the boss and the player is falling onto it from above.
In your L04-17 gravity script, the player has a velocity-y variable. When the player jumps, velocity-y starts positive (going up) and changes due to gravity until it goes negative (falling). In Scratch's coordinate system, "up" is positive — so when the player is moving downward onto the boss, velocity-y is negative.
if <<touching (Player v) ?> and <(velocity-y) > (0)>> then
change [boss-hp v] by (-1)
start sound [pop v]
wait (0.3) seconds
end
Putting all three together
The boss now has: HP that drops in measured chunks, two behaviours it cycles between, and a fair-but-tricky way for the player to win. When HP hits zero:
if <(boss-hp) = (0)> then
broadcast (victory v)
delete this clone
end
Worked Example — building the boss in nine steps 12 min
Open your level-3 project from SCR-L04-21. You should already have a player with gravity, a level variable, and a checkpoint sprite. We'll add the boss to level 3.
Step 1 — Paint the boss sprite
New sprite → Paint. Draw a larger, scarier version of your regular enemy — maybe a red rectangle with eyes, about twice the size of the player. Name the sprite Boss.
Step 2 — Make two variables
Variables palette → Make a Variable. Create boss-hp (For this sprite only) and boss-state (For this sprite only). Tick the checkbox on boss-hp only — that's the HP readout the player will see.
Step 3 — Hide the boss until level 3
On the Boss sprite: when ⚑ clicked → hide. The boss only appears when level 3 starts.
Step 4 — Listen for the boss-fight broadcast
Your level-loader (from SCR-L04-20) should already broadcast (start-boss-fight v) when the player reaches the boss room. On the Boss: when I receive (start-boss-fight v), then go to x: (180) y: (0) (the right side of the room), set [boss-hp v] to (5), set [boss-state v] to [patrol], show.
Step 5 — Build the state-driven main loop
Add a forever loop on the Boss. Inside, two if-blocks: one for patrol (slow walk, turn at edges), one for attack (jump upward). This is the boss's "body".
Step 6 — Build the state-switcher script
A second when I receive (start-boss-fight v) hat on the same sprite. A forever loop that sets state to patrol, waits 3 seconds, sets state to attack, waits 1 second, repeats. Two scripts on one sprite run at the same time — that's normal Scratch.
Step 7 — Add the stomp-damage check
A third script on the Boss (yes, three scripts, all on the same sprite, all running together): forever, if touching Player AND velocity-y > 0, then change HP by -1, play a sound, wait 0.3 seconds. The wait is the cooldown that stops the boss from taking 30 hits in one jump.
Step 8 — Add the win condition
Inside the same stomp script (or a fourth script — both work), check if boss-hp equals 0. If so, broadcast (victory v) and hide the boss.
Step 9 — Test the whole fight
Click the flag. Skip to level 3 (or use a temporary key like when [3 v] key pressed → broadcast (start-boss-fight v) for quick testing). Walk into the boss from the side — you should die. Jump on the boss from above — HP should drop by exactly 1. Five stomps later: victory.
The full assembled stack — script 1 of 3 (main loop)
when I receive (start-boss-fight v)
go to x: (180) y: (0)
set [boss-hp v] to (5)
set [boss-state v] to [patrol]
show
forever
if <(boss-state) = [patrol]> then
move (2) steps
if <touching (_edge_ v) ?> then
turn cw (180) degrees
end
end
if <(boss-state) = [attack]> then
change y by (10)
end
end
Script 2 of 3 (state switcher)
when I receive (start-boss-fight v)
forever
set [boss-state v] to [patrol]
wait (3) seconds
set [boss-state v] to [attack]
wait (1) seconds
end
Script 3 of 3 (stomp + win)
when I receive (start-boss-fight v)
forever
if <<touching (Player v) ?> and <(velocity-y) > (0)>> then
change [boss-hp v] by (-1)
start sound [pop v]
wait (0.3) seconds
end
if <(boss-hp) = (0)> then
broadcast (victory v)
hide
end
end
What you just built: the first enemy in your game that the player can't beat by accident. It has rules, it has phases, it has a measurable health bar. Every boss in every game — from Donkey Kong to Hollow Knight — is some variation of HP + state machine + a specific weakness.
Try It Yourself — three boss drills 15 min
Goal: Make the boss change colour when its HP drops below 3. Use the colour effect block so the player can see the boss is hurt — a classic "boss enters phase 2" visual cue.
when I receive (start-boss-fight v)
forever
if <(boss-hp) < (3)> then
set [color v] effect to (50)
else
set [color v] effect to (0)
end
end
Think: This is the same pattern as your stomp check — a forever loop watching a variable. You can stack as many of these "watcher" scripts on one sprite as you need.
Goal: Add a third state called rage. When HP drops to 2 or below, the boss switches to rage permanently — it moves at 5 steps instead of 2 and jumps every half-second instead of every 3 seconds. Force the state inside the state-switcher script using an extra check.
when I receive (start-boss-fight v)
forever
if <(boss-hp) > (2)> then
set [boss-state v] to [patrol]
wait (3) seconds
set [boss-state v] to [attack]
wait (1) seconds
else
set [boss-state v] to [rage]
end
end
if (boss-state) = [rage] then move (5) steps + change y by (10) branch in the main loop.Think: A state machine isn't limited to two states. Add a third, a fourth, a fifth. Boss fights in real games often have 3–5 phases, each more aggressive than the last — same pattern, more cases.
Goal: Give the boss a "spawn minion" attack. Every time the boss's state switches to attack, also broadcast spawn-minion. Create a tiny enemy sprite that listens for that broadcast and uses create clone of (myself v) at the boss's position. The minions chase the player. The boss is now harder.
when I receive (spawn-minion v)
go to (Boss v)
create clone of (myself v)
when I start as a clone
show
forever
point towards (Player v)
move (3) steps
if <touching (Player v) ?> then
broadcast (game-over v)
delete this clone
end
end
Think: Combining a boss with clone-spawned minions is the single biggest jump in challenge in a platformer. SCR-L04-24 (the capstone) is the place to actually wire all this together.
Mini-Challenge — the boss that can't be killed 5 min
"Iqbal's invincible boss"
Iqbal built a boss with HP and a stomp check. He tested it for ten minutes — the player can jump on the boss's head all they want, but the boss's HP never drops. Something is broken. Here's his stomp script:
when I receive (start-boss-fight v)
forever
if <touching (Player v) ?> then
if <(velocity-y) > (0)> then
change [boss-hp v] by (-1)
end
end
end
Iqbal swears the player is jumping on the boss. The two if-blocks look correct. What's actually wrong?
Reveal one valid solution
Two problems hiding in one script.
Bug 1: Iqbal's velocity-y variable lives on the Player sprite. When the Boss script reads (velocity-y) with no sprite name in front, Scratch looks for a local variable on the Boss — which doesn't exist, so it's treated as 0. 0 > 0 is false. The inner if-block never runs.
Fix: make velocity-y a "For all sprites" variable, or use ([velocity-y v] of (Player v)) to read it from the player.
Bug 2 (subtle): Even with bug 1 fixed, there's no cooldown. The instant the player jumps on the boss, the touching-check runs for many frames in a row (jumps last several frames), and HP drops by 5 in a single jump — instant kill. Add wait (0.3) seconds inside the inner if.
when I receive (start-boss-fight v)
forever
if <<touching (Player v) ?> and <([velocity-y v] of (Player v)) > (0)>> then
change [boss-hp v] by (-1)
wait (0.3) seconds
end
end
The fix: read velocity-y from the right sprite, and pace the damage with a wait. Variables in Scratch belong to a sprite unless you explicitly make them global — that's the rule that catches everyone, every project.
Recap 3 min
You built a boss. The three new ideas: HP (a variable that drops on each hit instead of dying instantly), a state machine (a string variable that flips between behaviours like patrol and attack), and a stomp check (touching and a velocity condition, so the boss is only vulnerable when the player jumps on it). Three scripts on one sprite run side-by-side: the body, the state-switcher, and the damage-and-win check. The instant HP hits zero, the boss broadcasts victory — and you'll wire up what receives that broadcast in the next lesson.
- Boss
- An enemy with HP, multiple behaviours, and (usually) a unique weakness. The end-of-level fight that tests everything the player has learned.
- HP (hit points)
- A number variable representing how much damage a sprite can take before dying. boss-hp starts at 5; each hit does change [boss-hp v] by (-1); when it reaches 0, the boss dies.
- State machine
- A pattern where a sprite holds a "state" word in a variable and uses if-blocks inside a forever loop to behave differently depending on which word it is. boss-state being
patrolorattackis a two-state machine. - Stomp
- A platformer hit-detection pattern: the player damages the enemy only when touching it while falling onto it. In code: <touching> and <velocity-y > 0>.
- Cooldown
- A short wait after an event fires, so the event doesn't fire many times in a row. Stops the boss from taking 30 damage in one jump.
Homework 2 min
The Mini-Boss Drill. Add a second, smaller boss to your project — a mid-level mini-boss that appears at the end of level 2, not level 3. Same pattern, smaller numbers.
- New sprite called
Mini-Boss. Smaller than the level-3 boss but still bigger than a regular enemy. - Two variables:
mini-boss-hp(start at 2) andmini-boss-state(just one state calledpatrol— no attack jump for this one). - Three scripts: main loop, state switcher (one state is fine), and a stomp-damage check with the same wait (0.3) cooldown.
- On HP = 0, broadcast
mini-boss-defeated. Use that broadcast in the level loader to unlock level 3.
Save as HW-L4-22-Mini-Boss.sb3. Click the flag. Walk to the end of level 2, jump on the mini-boss twice — level 3 should load.
Bring back next class:
- The
.sb3file. - Your answer to: "Why does HP = 2 feel different from HP = 5? How many hits would you give the final boss in a 10-minute play session? Why?"
Heads up for next class: SCR-L04-23 wires up the victory broadcast you sent from this boss. You'll build a title screen, an end screen ("YOU WIN! Score: 47"), and a slow-scrolling credits sprite. The fun polish stuff that makes a project feel finished.