Skip to main content

Stage 8: Eat or Be Eaten

Course progressStage 8 of 10
~90 min
Your workspace

Keep your Scratch project tab open all week. Open in a new tab so you don’t leave the course.

Build

a second clone-behavior script that detects touching and decides who eats whom

Learn

how two sprites finally talk through collision and broadcast

Ship

the climactic moment — the game becomes a real game

Teacher demo

Show the room — this is the moment the whole project clicks together:

  1. Click the Fish sprite. Show the EXISTING when I start as a clone script from Stage 7. "That one handles motion. We add a SECOND script that handles collision."
  2. Drag in another when I start as a clone block somewhere blank in the code area.
  3. Add wait until (touching Player) and (playerAlive = 1).
  4. Add if/else (enemySize < playerSize) then:
    • In if (smaller enemy = food): start sound chomp, change playerSize by (enemySize / 10), change fishEaten by 1, change score by (round enemySize), delete this clone.
    • In else (bigger enemy = danger): broadcast playerEaten.
  5. Click the green flag. Move the player into a small enemy. Watch it disappear, score goes up, player grows. Then move into a big one. Lose path runs.

That moment of player-growth-from-eating is the whole game becoming real.

The big idea

Today the swarm and the player finally talk to each other.

For three stages, the Player Fish has been swimming alone. The enemy swarm has been gliding past it. Neither one cares about the other. Today we change that with collision detection — Scratch's way of asking "is sprite A touching sprite B right now?"

When a clone touches the Player, two things might happen:

clone touches Player


compare sizes

├─── clone is SMALLER ─→ player eats it (chomp, grow, score)

└─── clone is BIGGER ─→ player loses (broadcast playerEaten)

The pattern is if/else: one branch for food, one branch for danger. Same touch, two completely different outcomes — driven by which sprite is bigger.

The new Scratch trick is that a clone can have MULTIPLE when I start as a clone scripts. They all run when the clone is born, in parallel. Stage 7's script handled motion (random spawn, glide, delete). Today's script handles collision (wait, compare, respond). One clone, two scripts running side by side.

When the player eats a smaller enemy, the script does four things at once:

  • Plays the Chomp sound (audio feedback)
  • Grows the player by enemySize / 10 (eating math)
  • Increments fishEaten by 1 (stat tracking)
  • Adds to score by the size of the meal (bigger meals = more points)

Then deletes the clone (it's been eaten).

When the player gets eaten by a bigger fish, the script broadcasts playerEaten — the message your Stage 4 lose handler is already listening for.

New words
collision detection
checking whether two sprites are touching
wait until
a Control block that pauses the script until a condition is true
touching
a Sensing block that asks 'am I overlapping with that sprite?'
if/else
branches the code into two paths based on a condition
round
an Operators block that rounds a number to the nearest whole number
Before you start

Stage 7 should be done — clones spawn with random costumes, colors, sizes, directions, and glide across the screen.

Build it

Step 1 — Add a second when-I-start-as-a-clone script

In the Fish sprite's code area, drag a FRESH when I start as a clone block somewhere blank — not connected to Stage 7's motion script.

Clones can run multiple when I start as a clone scripts in parallel. This new one will handle collision.

Step 2 — Wait until touching the player

The clone needs to pause until it actually bumps into the Player. We use wait until.

Drag wait until (<empty>) from Control under your new event block.

Now we build the condition. We want: "touching the Player AND the player is still alive."

From Operators, drag <empty> and <empty>.

Inside the LEFT slot of and: drag touching (Player) from Sensing. Pick Player from the dropdown.

Inside the RIGHT slot of and: drag <empty> = <empty> from Operators. On the left of the equals, drag playerAlive from Variables. On the right, type 1.

So the wait-until block now reads: wait until (touching (Player)?) and (playerAlive = 1).

The playerAlive = 1 check matters because after the player dies, the dead fish is still on stage briefly (the lose animation in Stage 4 takes 100 frames). Without that check, a clone could still try to "eat" the dead player — which would be weird.

Step 3 — Add the if/else size compare

Drag an if/else block under the wait-until.

Inside the diamond, build enemySize < playerSize:

  • From Operators, drag the < (less than) block.
  • On the left, drag enemySize from Variables.
  • On the right, drag playerSize from Variables.

So the if-block reads: "if the clone is smaller than the player, then..."

Step 4 — Fill in the IF branch (the clone is food)

The IF branch runs when the clone is smaller than the player. The player eats it.

Inside the if body, add these blocks in order:

Collision script

when I start as a clone
wait until touching Player? and playerAlive = 1
if enemySize < playerSize then
start sound chomp
change playerSize by enemySize / 10
change fishEaten by 1
change score by round enemySize
delete this clone
else broadcast playerEaten

How to build each:

  • start sound (chomp) — Sound block. Pick chomp from the dropdown (you added it to the Fish sprite in Stage 5).
  • change playerSize by (enemySize / 10) — drag the change block from Variables. Set the dropdown to playerSize. For the value, drag the / (divide) Operators block. On the left of the divide, drop enemySize. On the right, type 10.
  • change fishEaten by (1) — straightforward.
  • change score by (round (enemySize)) — change block on score. For the value, drag round (<empty>) from Operators. Inside round, drop enemySize. (Round makes sure the score is a whole number even if enemySize has decimals.)
  • delete this clone — the clone has been eaten, remove it.

Step 5 — Fill in the ELSE branch (the clone is too big)

The ELSE branch runs when the clone is bigger than the player. The player gets eaten.

Inside the else body, add just one block:

broadcast (playerEaten)

Choose playerEaten from the dropdown. (If it doesn't exist yet, choose New message and type playerEaten.)

This broadcast triggers the LOSE handler you built in Stage 4. The player rotates, plays the Bubbles sound, pixelates, and disappears.

We don't delete this clone — it stays on stage as evidence of who got the player. Stage 4's stop other scripts in sprite will handle cleanup on the Player side; the clone keeps gliding until its motion script's delete this clone runs naturally.

Step 6 — Test the full game loop

Click the green flag. Play!

  • Move your tiny Player around with the arrow keys.
  • When you bump into a smaller clone, you should hear chomp, see the score go up, fishEaten increment, and your fish visibly grow.
  • When you bump into a bigger clone, the Bubbles sound plays, the fish rotates, pixelates, and disappears. (That's the Stage 4 lose chain firing.)
  • If you eat enough small ones and playerSize crosses 100, the WIN chain from Stage 4 fires (Party Hat, Eggs sound, ghost fade).

You should now have a fully playable game.

Save your project. You just shipped real software.

Understand it

The most important idea today is that a single clone can run multiple when I start as a clone scripts at once. Stage 7's motion script and Stage 8's collision script BOTH run when each clone is born. They live in parallel — neither knows the other exists. The motion script handles "where the clone goes." The collision script handles "what happens if it bumps into the player." Two jobs, two scripts.

The reason we wait for (touching Player) and (playerAlive = 1) rather than just (touching Player) is defensive code. When the player loses (Stage 4), there's a 100-frame pixelate animation before the fish hides. During that animation, the dead fish is still technically on stage. Without the playerAlive = 1 check, a clone could touch the dying fish and trigger another playerEaten broadcast — which would re-fire the lose chain and look very weird. The check skips touching when the game is over.

The enemySize / 10 for growth is a critical tuning number. Without the divide-by-10, eating a size-50 enemy would jump the player's size from 15 to 65 — almost a win in one bite. Dividing by 10 keeps growth gradual: that same size-50 enemy adds 5 to the player's size. The player has to eat many small enemies to grow significantly. Small bites, real climb is the design rule.

The round enemySize for score is small but matters. enemySize is picked from pick random 5 to (playerSize + 20), which sometimes gives decimals like 23.7. Score is displayed as a whole number — without round, the score would show messy decimals like 23.7148 + 41.3209. Rounding keeps the display clean.

The else branch only broadcasts — no other cleanup. That's intentional. The Player's Stage 4 lose handler does everything: stop other scripts, play Bubbles, pixelate, broadcast gameover, hide. Centralizing the lose logic on the Player means the Fish doesn't need to know what "losing" looks like. Each sprite does what it knows; broadcast is the bridge.

The whole pattern — wait until touching → compare sizes → if/else → broadcast on bad outcome — is the same one used in every "eat-or-be-eaten" game ever made. Pac-Man uses it (with power pellets flipping who eats whom). Agar.io uses it. You just built a slice of the genre.

Try this

Learning beat

Try this

Three short experiments. Predict before you run, then test your guess.

Predict first

Remove the and (playerAlive = 1) from the wait-until condition. Predict what happens after the player loses (a clone touches the dying fish). Try it. What broke? Put it back.

Compare

Change change playerSize by (enemySize / 10) to change playerSize by enemySize (no divide). Click the green flag. How many enemies do you need to eat to reach playerSize > 100? Is the game still fun, or is it solved in one bite?

Connect

Stage 9 adds a backdrop, music, and a timer that tracks how long the game lasts. Look at your collision script. What single variable does your collision script update most often that the timer or stats screen might want to read?

Test your stage

  • Touching a smaller clone plays the chomp sound, makes the clone disappear, and grows the player.
  • Touching a bigger clone triggers the LOSE chain from Stage 4 (Bubbles, rotated fish, pixelate fade).
  • The fishEaten readout on stage goes up each time you eat.
  • The score readout goes up each time you eat (by roughly the enemy's size).
  • Eating enough small enemies grows playerSize past 100, which triggers the WIN chain (Party Hat, Eggs).
  • You can play a full round end-to-end (win OR lose) without anything crashing.
  • Your project is saved.
  • Design check. Play three rounds in a row. Does the game feel fair? Are there enough small enemies to grow on without getting eaten by big ones?

If it breaks

  • Touching a small clone does nothing. Three suspects. First, is your wait until waiting on touching (Player) (capital P matters)? Second, is enemySize < playerSize the right direction (smaller enemy, less-than)? Third, is the if/else's body block-set actually filled in (not empty)?
  • Touching a big clone does nothing. The else branch is empty, or the broadcast (playerEaten) dropdown points at the wrong message. Confirm the dropdown says playerEaten exactly (camelCase, no space).
  • The chomp sound doesn't play. Did you add Chomp to the Fish sprite's Sounds tab in Stage 5? It needs to be on the Fish, not the Player.
  • Player gets eaten but never dies. The Stage 4 lose chain isn't catching the broadcast. Check that the broadcast name in this stage (playerEaten) matches exactly what Stage 4's when I receive listens for. Same camelCase spelling on both sides.
  • Player size jumps wildly when eating. The /10 divide is missing or the value is different. Drag the divide operator back in and make sure both slots are filled (enemySize on left, 10 on right).
  • Score updates with messy decimals. The round block is missing around enemySize in the change score by line. Add round to keep the score whole.
  • Game keeps "eating" after I lose. The and (playerAlive = 1) check is missing from the wait-until. Add it.
Coach notes

This is the climactic stage. The whole project comes together. Plan to celebrate the moment when each kid eats their first small enemy and visually grows. That growth is the "wow" of the entire course.

The single most common failure: kids put the Stage 8 script in the wrong sprite. The collision logic goes on the Fish (enemy) sprite, not the Player. Walk the room and confirm before they hit Step 6.

The second most common: the touching dropdown not set to Player. Without it, the wait-until never fires. Have campers click the dropdown and confirm.

The third most common: the broadcast name is playerEaten camelCase, exactly. If it's playereaten (all lowercase) or Player Eaten (with a space), it won't match Stage 4's listener. Have kids read both broadcasts aloud and confirm they match.

For pacing: Step 1–3 (the setup blocks) should take ~30 min. Step 4 (the IF branch) is dense — give it 30 min. Step 5–6 (else + test) should fly. The last 20 min is gameplay testing and tuning, which is fun.

If a camper finishes by minute 70, push them into the medium stretch (tuning). The hard stretch (combo bonus) is great prep for Stage 10 — only push if they're way ahead and have the discipline to keep the codebase clean.

This is the natural mid-course celebration moment. Verbally acknowledge what they just built: "You wrote your first complete game loop."