Learning Goals 3 min
By the end of this lesson you will be able to:
- Explain why a "flat-bounce" Breakout — where the ball just reflects off the paddle without any angle change — feels boring, and why angle-by-offset bouncing feels like a game.
- Compute the offset between the ball and the paddle using () - (): ((x position) - ([x position v] of (Paddle v))), and reason about whether it is negative (ball is left of paddle centre), zero (dead centre), or positive (ball is right of centre).
- Combine that offset with a multiplier to point in direction (...) after a paddle hit — giving the player aim by sliding the paddle.
Warm-Up — why flat bouncing is boring 7 min
Last lesson you built the wall. This lesson the ball returns. Let us start with the simplest possible ball, and feel exactly why it is not good enough.
when flag clicked
go to x: (0) y: (-100)
point in direction (45)
forever
move (5) steps
if on edge, bounce
if <touching (Paddle v) ?> then
point in direction ((180) - (direction))
end
end
Predict puzzle. You play with this ball for two minutes. What do you notice about how it feels to control?
Reveal the answer
It feels random and powerless. The ball comes down at some angle, hits your paddle, and goes back up at the mirror angle. You have no influence on where it goes next. If the ball was going up-and-left when it hit, it will go down-and-left forever — you are just a wall it occasionally bumps into. In real Breakout (and in every brick-breaker since), the player can aim by where on the paddle the ball lands. Hit the right side of the paddle — ball goes right. Hit the centre — ball goes straight up. That single design choice is what makes Breakout playable for hours instead of minutes.
Today we add that aim. The whole trick is one subtraction and one multiplication.
New Concept — angle by offset 15 min
The idea: when the ball touches the paddle, find out where on the paddle it hit, and use that to pick the new direction.
Step one — measure the offset
The ball has an x position. The paddle has an x position too — we can read another sprite's x using [x position v] of (Paddle v). Subtract one from the other:
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
If the paddle is 80 pixels wide, the offset will range from about −40 (ball on far left of paddle) to +40 (ball on far right). At the dead centre, offset = 0.
Step two — pick a direction
In Scratch, direction 0 means straight up, 90 is right, 180 is down, −90 (or 270) is left. We want the ball going up, but tilted left or right depending on offset:
- Offset = 0 → direction = 0 (straight up).
- Offset = +40 → direction ≈ +80 (way to the right).
- Offset = −40 → direction ≈ −80 (way to the left).
That is a multiplier of about 2. The formula is:
point in direction ((offset) * (2))
Putting it together inside the bounce
Inside the ball's forever loop, when the paddle is touched, we read the offset and re-point the ball:
if <touching (Paddle v) ?> then
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
point in direction ((offset) * (2))
change y by (5)
end
The sticky-ball bug
Without that change y by (5) nudge, the ball would touch the paddle, change direction, take one move step, and still be touching the paddle — so the if-then fires again, changes direction again, and the ball gets stuck "vibrating" on top of the paddle. The 5-pixel push is just enough to break contact.
Worked Example — wiring the smart bounce 12 min
Open the project from last lesson (or a fresh project with a paddle and a ball — either works). We will replace the flat-bounce script with the angle-by-offset version in eight steps.
Step 1 — Make sure the paddle is ready
Click the Paddle sprite. Its script should be the L04-31 paddle: when ⚑ clicked, go to x: (0) y: (-150), then a forever with set x to (mouse x). Nothing else needs to change on the paddle today.
Step 2 — Create the offset variable
Click the Ball sprite. Variables palette → Make a Variable → name it offset, scope For this sprite only. Tick its checkbox so you can see it on the Stage while testing.
Step 3 — The ball start-up
Drag when ⚑ clicked. Then go to x: (0) y: (0), then point in direction (180) so the ball starts heading down toward the paddle.
Step 4 — Open the forever loop
Add forever below the start-up. The next four blocks all live inside this loop.
Step 5 — Move and edge-bounce
Inside the forever: move (5) steps then if on edge, bounce. These handle the walls and the ceiling.
Step 6 — The paddle if-then
Inside the forever, after the edge bounce, add if <> then. Drop touching (Paddle v)? into the diamond.
Step 7 — Inside the if-then: read the offset
First block inside the if-then: set [offset v] to ((x position) - ([x position v] of (Paddle v))). The subtraction comes from Operators; [x position v] of (Paddle v) comes from the Sensing palette's "of" reporter.
Step 8 — Inside the if-then: point and nudge
Next: point in direction ((offset) * (2)). Last: change y by (5) to break contact. That is the whole script.
Click the flag. The ball drops, the paddle follows your mouse — and when the ball lands, it shoots back up at an angle that you chose. Try landing it dead-centre and watch it go straight up. Then slide the paddle to the left at the last moment and watch the ball go up-and-right. You just gave the player aim.
The full assembled ball script
when flag clicked
go to x: (0) y: (0)
point in direction (180)
forever
move (5) steps
if on edge, bounce
if <touching (Paddle v) ?> then
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
point in direction ((offset) * (2))
change y by (5)
end
end
What you just built: a piece of game feel. Game feel is the invisible part of a game — the tiny mathematical choices that make controls feel responsive instead of frustrating. Multiplier 2 feels great. Multiplier 0 (today's warmup) feels dead. Multiplier 8 feels chaotic. Designers tune these numbers for hours. You now have one of those numbers in your hand.
Try It Yourself — three physics drills 15 min
Goal: Tick the box next to the offset variable on the Stage so its current value is visible while you play. Run the game and watch the number. Land the ball on the left edge of the paddle — what value do you see? Then land it on the right edge.
when flag clicked
go to x: (0) y: (0)
point in direction (180)
forever
move (5) steps
if on edge, bounce
if <touching (Paddle v) ?> then
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
point in direction ((offset) * (2))
change y by (5)
end
end
Think: Negative numbers when the ball is to the left of the paddle's centre, positive when to the right. The variable monitor on the Stage is one of the best debugging tools in Scratch — use it often.
Goal: Experiment with the multiplier. Change ((offset) * (2)) to ((offset) * (1)), play for a minute, then try ((offset) * (4)). Decide which multiplier feels best to you. There is no correct answer — game designers run this exact experiment.
if <touching (Paddle v) ?> then
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
point in direction ((offset) * (4))
change y by (5)
end
Think: Why does multiplier 1 feel sluggish? Because the maximum offset (about 40) only produces a 40° lean — never very sideways. Multiplier 4 gives up to ±160° which actually wraps around to back downward — too sharp. Multiplier 2 hits the sweet spot.
Goal: Make the ball speed up a little each time it bounces off the paddle. Add a speed variable that starts at 5 and grows by 0.2 every paddle bounce. Replace move (5) steps with move (speed) steps. (This is how real Breakout gets harder over time.)
when flag clicked
set [speed v] to (5)
go to x: (0) y: (0)
point in direction (180)
forever
move (speed) steps
if on edge, bounce
if <touching (Paddle v) ?> then
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
point in direction ((offset) * (2))
change y by (5)
change [speed v] by (0.2)
end
end
Think: Real Breakout caps the speed somewhere (otherwise it becomes unplayable). Add an if-then: if <(speed) > (12)> then set [speed v] to (12).
Mini-Challenge — Aisha's ghost ball 5 min
"The ball goes through my paddle"
Aisha built today's script but the ball keeps slipping through her paddle as if the paddle were a ghost. Here is her ball script:
when flag clicked
go to x: (0) y: (0)
point in direction (180)
forever
move (25) steps
if on edge, bounce
if <touching (Paddle v) ?> then
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
point in direction ((offset) * (2))
change y by (5)
end
end
Reveal one valid solution
Aisha is moving the ball 25 steps per frame. The paddle is only 8 pixels tall. On any given frame, the ball is either above the paddle, or below it — it almost never lands inside the paddle because it leaps right over it. The if-then never sees touching (Paddle v)? as true.
This is called tunnelling — when a fast-moving object passes through a thin object because the collision check happens between the two positions. Every physics engine in every game has to deal with it. The simplest fix is to keep the move-per-frame smaller than the thinnest collider.
Fix: slow the ball down to move (5) steps, or make the paddle taller (say 16 pixels). Either works. In our worked example we kept move at 5 for exactly this reason.
when flag clicked
go to x: (0) y: (0)
point in direction (180)
forever
move (5) steps
if on edge, bounce
if <touching (Paddle v) ?> then
set [offset v] to ((x position) - ([x position v] of (Paddle v)))
point in direction ((offset) * (2))
change y by (5)
end
end
The lesson: in any frame-based game, your fastest object must move less per frame than your thinnest target is wide. Otherwise it tunnels through. Remember this when the L04-35 power-up "fast ball" tempts you to crank the speed.
Recap 3 min
You added aim to the ball. The trick was to measure offset — the horizontal distance from the ball to the paddle's centre — and use it inside point in direction ((offset) * (2)) to choose the new flight angle. Hitting the centre sends the ball straight up; hitting the edge sends it sharply sideways. A single tiny change y by (5) after the bounce prevents the ball from sticking. This is the simplest example of game feel — invisible maths that transforms how a game feels to play.
- Offset
- A measured distance between two positions, used as input to another calculation. Today's offset was (x position) - ([x position v] of (Paddle v)).
- Game feel
- The collection of tiny numerical choices (multipliers, speeds, delays) that make a game's controls feel responsive and pleasing. Hard to write down, easy to feel.
- Reading another sprite
- Using the (... v) of (Sprite v) reporter to fetch a property of a different sprite. Lets sprites cooperate without broadcasting.
- Tunnelling
- The bug where a fast-moving object skips over a thin object between two frames, missing the collision entirely. Fix: slow the object, fatten the target, or both.
- Nudge
- A small position change applied after a collision response to prevent the collision from re-triggering on the next frame. Today's nudge was change y by (5).
- Direction (in Scratch)
- An angle in degrees where 0 is up, 90 is right, 180 is down, −90 is left. Different from a maths textbook — remember the up-is-zero convention.
Homework 2 min
The Two-Paddle Pong. Use today's offset trick in a different game: classic two-player Pong. Build a vertical Pong (paddle on the bottom of the Stage, paddle on the top) where the ball bounces off both paddles using the angle-by-offset formula.
- Open a new project. Save as
HW-L4-33-Pong.sb3. - Make two paddle sprites:
Paddle-Bottom(controlled by mouse x) andPaddle-Top(controlled by arrow keys, or AI: set x to (x position of Ball)). - Ball script: same as today's, but the if-then needs two checks — touching the bottom paddle OR the top paddle. Use <> or <>.
- After each paddle hit, compute the offset against the paddle that was hit (use two separate if-thens, one per paddle, so each can read its own paddle's x).
- Test it. Both paddles should give the player aim.
Bring back next class:
- The
.sb3file. - Your answer to: "Which multiplier (1, 2, 3, or 4) felt best for Pong? Was it the same one you liked best for Breakout?"
- Any moment where you spotted tunnelling. Write down the speed setting that caused it.
Heads up for next class: SCR-L04-34 introduces power-ups — short-term bonuses dropped by destroyed bricks. We will use a Scratch list to remember which power-ups exist and broadcast events to apply them. The ball physics you just built is the foundation; power-ups will tweak the very same speed and multiplier.