Learning Goals 3 min
By the end of this lesson you will be able to:
- Add a jump impulse to last lesson's gravity engine by setting velocity-y to a positive value when the spacebar is pressed.
- Maintain an on-ground flag each frame using touching color [#964b00]?, and use it to block double-jumps.
- Explain why gravity must be skipped when the player is on the ground — and why missing that check causes the player to sink through platforms.
Warm-Up — predict the rocket cat 7 min
Last lesson (SCR-L03-39) you built a gravity engine with a velocity-y variable. Every frame, gravity pulls velocity down by 1, and the player's y-position changes by velocity. Lovely. But there is no way to go up. Here is one attempt to add a jump:
when flag clicked
forever
if <key [space v] pressed?> then
set [velocity-y v] to (12)
end
change y by (velocity-y)
change [velocity-y v] by (-1)
end
The player is sitting on a brown platform. You hold down the spacebar. What happens?
Reveal the answer
The player flies off the top of the Stage and never comes back. Every frame the spacebar is still down, so every frame velocity-y is reset back to 12. Gravity tries to drop it (12 → 11 → 10), but the next frame the if-then slams it back to 12. The player rises 12 pixels per frame, forever. The first job of this lesson is to make sure a jump only kicks once — only when the player is on the ground.
Today you'll add a single boolean variable, on-ground, that solves both the rocket-cat bug and the "sinking into the platform" bug. Two bugs, one flag.
New Concept — the on-ground flag 15 min
Real platformers — Mario, Celeste, Hollow Knight — all share one tiny secret: every frame, the engine asks "is the player standing on something solid right now?" and stores the yes/no answer in a variable. Every other movement rule (jump, gravity, walk-off-the-edge) looks at that variable to decide what to do. We'll call ours on-ground.
What "on-ground" actually means
In Scratch we don't have proper collision rectangles, so we use a colour trick. Paint every platform with one specific brown — #964b00 works well. Then ask:
set [on-ground v] to <touching color [#964b00] ?>
The hexagonal touching color [#964b00]? reports true or false. set [on-ground v] to () happily stores either of those in a variable. We don't need a separate if-then to do it — the boolean flows straight in.
Rule 1 — only jump when on-ground
A jump is a sudden kick of upward velocity. In our engine "up" means positive y, so we set velocity-y to a positive number like 12. But we only allow it when the flag is true:
if <<key [space v] pressed?> and <(on-ground) = [true]>> then
set [velocity-y v] to (12)
end
After the kick, gravity does the rest. Velocity goes 12 → 11 → 10 → 9 → ... → 1 → 0 → −1 → −2 → ... and the player rises, slows, peaks, and falls back down. You don't write a single "go up then come down" block — gravity is already doing that work.
Rule 2 — skip gravity when on-ground
Here is the subtler half. If gravity keeps subtracting 1 from velocity-y every frame even while the player is standing still on a platform, velocity-y becomes −1, −2, −3, ... and the player slowly sinks into the platform. By frame 60 the player is buried.
The fix is to clamp velocity-y back to 0 (and skip the gravity tick) whenever on-ground is true:
if <(on-ground) = [true]> then
set [velocity-y v] to (0)
else
change [velocity-y v] by (-1)
end
Order matters
The full forever loop runs in this order every frame: update the flag → check jump input → apply gravity (or freeze it) → move by velocity. If you scramble the order you get weird bugs. The most common scramble is checking the jump before updating on-ground — which means the jump check uses last frame's flag value. Usually fine, but occasionally lets a double-jump sneak through right at the edge of a platform.
Worked Example — Pak Ali jumps a parit 12 min
We'll build a player sprite that walks, jumps, and lands cleanly on a brown platform. Open PlatformerArc.sb3 from last lesson — it should already have the gravity engine and a brown platform.
Step 1 — Make the variable
Variables palette → Make a Variable → name it on-ground → For this sprite only → OK. Uncheck the watcher on the Stage to keep things tidy. (Tick it on while debugging.)
Step 2 — Update the flag every frame
Open your existing forever loop. As the first block inside the forever, drop set [on-ground v] to (). Drag touching color [#964b00]? from Sensing straight into the empty slot. Eyedrop the colour off your brown platform to match exactly.
Step 3 — Add the jump check
Below the flag update, add an if <> then. From Operators, drop in <> and <>. Left slot: key [space v] pressed?. Right slot: () = () with on-ground on the left and the word true on the right.
Step 4 — The kick
Inside the if-then, add set [velocity-y v] to (12). This is the jump impulse. Higher number = higher jump. We'll tune it later.
Step 5 — Conditional gravity
Below the jump if-then, add an if <> then else. Diamond: () = () with on-ground = true. Inside the if body: set [velocity-y v] to (0). Inside the else body: change [velocity-y v] by (-1).
Step 6 — Apply the velocity
Last block inside forever: change y by (velocity-y). The variable round-reporter slides into the number slot. This is what actually moves the player.
Step 7 — Test on flat ground
Hit the flag. The player should sit perfectly still on the brown floor (on-ground is true, velocity is clamped to 0). Press space. The player should pop up about 70 pixels, hang for a moment, and fall back down. Press space again mid-air — nothing should happen.
Step 8 — Test the parit (drain)
Draw a gap in your floor — delete a strip of brown between two platforms. Walk the player to the edge of the first platform. The moment they leave the brown, on-ground becomes false, gravity kicks in, and they fall into the gap. Jump before the edge and they sail across.
The full assembled stack
when flag clicked
set [velocity-y v] to (0)
forever
set [on-ground v] to <touching color [#964b00] ?>
if <<key [space v] pressed?> and <(on-ground) = [true]>> then
set [velocity-y v] to (12)
end
if <(on-ground) = [true]> then
set [velocity-y v] to (0)
else
change [velocity-y v] by (-1)
end
change y by (velocity-y)
end
What you just built: the core motion code of every 2D platformer. Mario uses a fancier version of this exact pattern. The arrow keys / walk code from earlier lessons sits alongside it untouched — left/right is horizontal, jump/gravity is vertical, they don't interfere.
Try It Yourself — three jump drills 15 min
Goal: Make a "super jump" version. Change one number so the player jumps roughly twice as high. Then try a "moon jump" — what number makes the player float almost off the top of the Stage?
if <<key [space v] pressed?> and <(on-ground) = [true]>> then
set [velocity-y v] to (20)
end
Think: Doubling the velocity does not double the jump height — gravity has more time to slow you, so the height roughly triples. Try 24 and see how absurd it looks.
Goal: Let the player use the up arrow as well as space. The jump should fire if either key is pressed (and they're on the ground). Use the <> or <> joiner from Operators.
if <<<key [space v] pressed?> or <key [up arrow v] pressed?>> and <(on-ground) = [true]>> then
set [velocity-y v] to (12)
end
Think: Real game controllers let players use whichever button feels natural. Add a third option — what about W? Just expand the or chain.
Goal: Add a jump sound that plays exactly once per jump — not 60 times a second while the spacebar is held. Hint: play the sound inside the same if-then that sets velocity-y to 12. Because that if-then only runs at the instant of takeoff, the sound only fires once.
if <<key [space v] pressed?> and <(on-ground) = [true]>> then
set [velocity-y v] to (12)
play sound [Boing v] until done
end
Think: This is the same "fence" idea from L02-06 — anything inside the if-then runs only at the moment the condition flips true. Outside the fence, the sound would spam.
Mini-Challenge — Hafiz's sinking hero 5 min
"My player keeps disappearing into the floor"
Hafiz copied the gravity engine from SCR-L03-39 and added a jump on top. It looks right but after about five seconds of standing still, the player slowly sinks into the brown platform and vanishes off the bottom of the Stage. Here is his stack:
when flag clicked
set [velocity-y v] to (0)
forever
set [on-ground v] to <touching color [#964b00] ?>
if <<key [space v] pressed?> and <(on-ground) = [true]>> then
set [velocity-y v] to (12)
end
change [velocity-y v] by (-1)
change y by (velocity-y)
end
Click the flag mentally — what does velocity-y do, frame by frame, while the player just stands on the brown floor?
Reveal one valid solution
Hafiz applies gravity unconditionally. Frame 1: velocity-y goes from 0 to −1, player moves down 1 pixel. Frame 2: −1 to −2, player moves down 2 more pixels. Frame 3: down 3 more. After 60 frames velocity-y is −60 and the player is sinking 60 pixels per frame. The on-ground touching-colour check is keeping the variable correct, but the gravity line ignores it.
The fix is to gate gravity behind the same flag, and clamp velocity-y to 0 on the ground:
when flag clicked
set [velocity-y v] to (0)
forever
set [on-ground v] to <touching color [#964b00] ?>
if <<key [space v] pressed?> and <(on-ground) = [true]>> then
set [velocity-y v] to (12)
end
if <(on-ground) = [true]> then
set [velocity-y v] to (0)
else
change [velocity-y v] by (-1)
end
change y by (velocity-y)
end
One extra if-then-else. Now velocity-y is frozen at 0 whenever the player is on brown, so they sit perfectly still. The moment they walk off the edge, on-ground flips false and gravity kicks back in. Conditional gravity is what makes a platformer feel solid instead of slippery.
Recap 3 min
You added a jump to last lesson's gravity engine. The trick is the on-ground flag — one boolean variable, refreshed every frame from touching color [#964b00]?. Two rules look at that flag: the jump only fires when on-ground is true (no flying), and gravity only ticks when on-ground is false (no sinking). The jump itself is one block — set [velocity-y v] to (12) — because the gravity loop already handles the up-slow-down-fall arc for free. Next lesson you'll add multiple level layouts using different backdrops.
- Jump impulse
- The single positive number assigned to velocity-y at the instant the player jumps. 12 is a comfortable default; 20 is a super-jump.
- On-ground flag
- A boolean variable updated every frame to record whether the player is currently touching a platform. Used to gate both jumping and gravity.
- Conditional gravity
- Applying the gravity tick (change [velocity-y v] by (-1)) only when the player is in the air. Without this, the player slowly sinks through solid ground.
- And-gate
- The <> and <> operator. Both inputs must be true for the whole block to report true. Used here to require both space pressed and on the ground.
- Touching colour
- The touching color [ ]? sensing block. Reports true if any pixel of the sprite's costume overlaps any pixel of the chosen colour. The cheapest platform-collision technique in Scratch.
Homework 2 min
The Two-Platform Hop. Build a tiny one-screen level with exactly two brown platforms separated by a gap the player must jump across.
- Open the project from class. Edit the Stage backdrop and draw two brown rectangles — one on the left half, one on the right half, with a gap of about 80 pixels between them. Both rectangles must use the exact same brown (eyedrop to be sure).
- Confirm the player walks off the left platform and falls down the gap (on-ground turns false, gravity wins).
- Tune the jump impulse so the player can just barely clear the gap with a single jump. Try 11, 12, 13, 14 — find the smallest number that works.
- Add a when ⚑ clicked + go to x: (-180) y: (0) so each test starts the player on the left platform.
Save as HW-L3-40-Two-Platform-Hop.sb3. Record the smallest jump impulse that clears the gap — you'll compare with the class next time.
Bring back next class:
- The
.sb3file. - Your answer to: "What happens if you delete the set [velocity-y v] to (0) line inside the on-ground branch? Try it for ten seconds. Describe what your player does."
Heads up for next class: SCR-L03-41 uses Scratch backdrops to build multiple levels in one project. Your hero will be able to walk off the right edge of level 1 and arrive at the start of level 2 — a tiny step toward a real game.