6.3 Physics Applications

Aligned outcomes:

SLO 3

Describe, design, implement, and test structured programs using currently accepted methodology.

Learning Objectives

By the end of this section, you will be able to:

In this section, you will learn to:
  • Adjust gravity to create different game feels.
  • Read and write a sprite's .vel vector to control movement.
  • Detect keyboard input with kb.pressing() and kb.presses().
  • Implement a jump with a ground check.
  • Push a sprite with applyForce and explain how that differs from setting .vel.
  • Use bounciness and friction to change how a sprite behaves on contact.
  • Rotate a sprite and give it spin.

This section takes §6.2's coin-and-bomb pickup game — a floating player, a coins group, a bombs group, a running score — and puts it on solid ground. By the end, the player walks, jumps, and gets knocked back by bombs, instead of floating freely over the pickups.

6.3.1 Gravity in Different Game Types

world.gravity.y pulls every dynamic sprite downward. 10 feels roughly earth-like; set it to 0 for top-down games, or try negative values for "upside-down" worlds.

Definition 6.3.1: Gravity

A force that pulls dynamic sprites in a direction. In moSHion, world.gravity.y controls vertical gravity. Positive values pull downward; zero means no gravity; negative values pull upward.

Try It Now 6.3.1

Here's §6.2's pickup game with one addition: a static ground sprite. Try world.gravity.y at 0 (the pickup game's original setting), then 10. What does the player do differently now that a floor exists?

Editor
runs in a sandboxed frame
▶ Press Run to see the output…
Solution

At world.gravity.y = 0, the player hangs at its starting height — this is §6.2's original setup, where the player only ever moved by keyboard input you haven't added yet. At world.gravity.y = 10, the player falls and lands on ground, sitting well above the row of coins and bombs at y = 340. The rest of this section is about getting the player back down to that row on purpose, instead of by accident.

6.3.2 Velocity and Player Control

Each sprite has a .vel vector with .x and .y components, measured in pixels per frame. Reading .vel tells you how fast a sprite is moving; writing it overrides physics for that frame. Assigning vel every frame is fine for player controls — you're telling the engine exactly what you want.

Definition 6.3.2: Velocity

A vector (two numbers: x and y) that describes how fast and in what direction a sprite is moving, measured in pixels per frame.

Try It Now 6.3.2

With gravity back at 10, set player.vel.x = 2 every frame in draw. Watch the player drift right as it falls, passing near the coins and bombs on the way down instead of landing straight on the ground below its start position.

Editor
runs in a sandboxed frame
▶ Press Run to see the output…
Solution
Editor
runs in a sandboxed frame
▶ Press Run to see the output…

The player drifts right while falling, sweeping through part of the pickup row and lands, still drifting, off the right edge of the ground eventually. Direct control like this is a stopgap — §6.3.3 replaces it with keys the player actually presses.

6.3.3 Keyboard-Driven Movement

kb.pressing('left') returns true as long as the left arrow key is held down. kb.presses(' ') returns true only on the frame the space bar is first pressed (not held). Use pressing for continuous actions (moving) and presses for one-shot actions (jumping).

Definition 6.3.3: kb.pressing vs. kb.presses

kb.pressing(key) returns true every frame the key is held down. kb.presses(key) returns true only on the first frame the key is pressed.

Try It Now 6.3.3

Replace the constant drift from 6.3.2 with real left/right control: kb.pressing('left')/kb.pressing('right'), stopping when neither is held. Steer the player along the pickup row once it lands.

Editor
runs in a sandboxed frame
▶ Press Run to see the output…
Solution
Editor
runs in a sandboxed frame
▶ Press Run to see the output…

Now the player falls straight down, lands on the ground, and the arrow keys walk it back and forth through the coins (score climbs) and bombs (score drops) sitting at y = 340. Releasing both keys stops the player exactly where it is.

6.3.4 Jumping with Collision Detection

A jump needs two things: (1) detect the space bar press, and (2) check that the player is on the ground so they can't jump in mid-air. player.colliding(ground) returns true when the player sprite is touching the ground sprite.

Setting player.vel.y = -8 gives an upward burst. Gravity pulls the player back down, creating an arc.

The ground check prevents double-jumping. Without player.colliding(ground), pressing space while in the air would set vel.y = -8 again, canceling the downward pull and letting the player fly. The check ensures the jump only fires when the player has something solid beneath them.

Try It Now 6.3.4

Add a jump to your walking player. While you're at it, raise the coins and bombs row so a jump is required to reach some of them — try y = 300 instead of 340.

Editor
runs in a sandboxed frame
▶ Press Run to see the output…
Solution
Editor
runs in a sandboxed frame
▶ Press Run to see the output…

The pickup row now sits closer to head height, so walking through it collects everything without a jump needed — but the jump is there, ground-checked, ready for a taller obstacle. Try raising the row further, to y = 250, and the jump becomes the only way to reach it.

6.3.5 Forces: Pushing Instead of Setting

Everything so far moved the player by assigning to .vel — "you are now going 4 pixels a frame to the right." That is direct control, and it is exactly right for a player responding to arrow keys, because the player should stop the instant the key is released.

It is wrong for a knockback. When the player hits a bomb, you don't want to set a new speed — you want to push the player away from the danger and let physics carry them, on top of whatever movement they were already doing. applyForce(fx, fy) is that push.

Definition 6.3.4: Force

A push applied to a sprite with applyForce(fx, fy). Unlike setting .vel, a force adds to whatever motion the sprite already has, and the physics engine works out the resulting speed.

The units are not pixels. .vel is measured in pixels per frame, so vel.x = 4 is a speed you can picture. applyForce takes Newtons — the force units the physics engine works in — and there is no conversion between the two. Expect to try a number, watch it, and multiply by ten.

Try It Now 6.3.5

Give the bomb hit a real knockback: when player.overlaps(bombs, ...) fires, in addition to subtracting 5 points, push the player away from the bomb with applyForce. Push in the opposite x-direction from the bomb relative to the player.

Editor
runs in a sandboxed frame
▶ Press Run to see the output…
Solution
Editor
runs in a sandboxed frame
▶ Press Run to see the output…

Hitting a bomb now shoves the player up and away from it, on top of whatever left/right motion the arrow keys were already applying — because applyForce adds to existing motion instead of replacing it. Walking into a bomb from the left sends the player right; from the right, left.

6.3.6 Bounciness and Friction

Drop a ball on the floor in moSHion and it lands with a thud and stays there. That surprises everyone, and it is not a bug — both bounciness and friction start at 0.

A sprite with bounciness = 0 keeps none of its speed in a collision. A sprite with friction = 0 slides forever once moving.

Definition 6.3.5: Bounciness

How much of its speed a sprite keeps after a collision, from 0 (keeps none — a beanbag) to 1 (keeps all — a superball that never settles). The default is 0.

Definition 6.3.6: Friction

How strongly a sprite resists sliding along a surface it is touching. 0 is ice; higher values bring a sliding sprite to a stop. The default is 0.

Both belong to each sprite, and a contact involves two of them — so a bouncy player on a dead floor still bounces, and giving the floor friction slows anything sliding across it.

Try It Now 6.3.6

Give player a bounciness of 0.5 and ground a friction of 0.4. Land, then jump straight up without moving sideways and watch how differently the landing feels compared to §6.3.4.

Editor
runs in a sandboxed frame
▶ Press Run to see the output…
Solution
Editor
runs in a sandboxed frame
▶ Press Run to see the output…

A plain jump now lands with a small rebound instead of stopping dead. The bomb knockback from §6.3.5 feels noticeably bouncier too — bounciness applies to every landing, not just the jump. ground.friction is what eventually brings a sideways slide to a stop rather than letting it carry forever.

6.3.7 Rotation and Spin

Sprites turn as well as move. rotation is which way a sprite is facing, in degrees. angularVelocity is how fast it is turning, in degrees per frame.

Try It Now 6.3.7

Add a decorative spinning warning sign above the bombs: a small static sprite with angularVelocity = 3 positioned a little above each bomb. It should not affect gameplay — just spin as a visual cue that something dangerous is nearby.

Editor
runs in a sandboxed frame
▶ Press Run to see the output…
Solution
Editor
runs in a sandboxed frame
▶ Press Run to see the output…

Each warning sign spins steadily above its bomb. It's 'static' and never referenced by any collision check, so it's purely decoration — a good habit to notice: not every sprite in a scene needs to interact with anything.

6.3.8 Challenge: Extend It Yourself

No starter code this time — you build it. Add a third bomb somewhere the player has to jump to avoid landing directly on it, and give it a bigger knockback (applyForce magnitude at least double the other two). Then add one more coin placed so that collecting it requires walking underneath a spinning warning sign without touching the bomb it marks.

Hint (try the Challenge yourself first!)

Reuse the exact bombs.Sprite(...) + warning-sign pattern from Try It Now 6.3.7's solution for the new bomb, just with a bigger applyForce multiplier in the callback for that specific one — you'll need to check which bomb was hit, e.g. by comparing b.x to the new bomb's known x-position.

Problem Set 6.3

Problem 1. What does world.gravity.y = 0 do? What kind of game would use this setting?

Solution

Step 1 — Recall what gravity does: world.gravity.y pulls every dynamic sprite in the vertical direction each frame.

Step 2 — Interpret the value 0: Setting it to 0 means there is no downward pull at all, so sprites stay wherever they are placed (or keep moving only under forces you apply directly).

Step 3 — Identify suitable games: Top-down games — a maze game viewed from above, a Pac-Man-style game, or any game where "up" and "down" on screen aren't directions things should fall. Gravity would make characters slide to the bottom of the screen for no reason.

Answer: world.gravity.y = 0 turns off vertical gravity entirely; sprites don't fall. It suits top-down games (mazes, arcade classics) where falling makes no sense.

Problem 2. What is the difference between kb.pressing('space') and kb.presses('space')? Give an example of when you would use each one.

Solution

Step 1 — Define kb.pressing: It returns true every frame while the key is held down — continuous behavior.

Step 2 — Define kb.presses: It returns true only on the first frame the key is pressed — one-shot behavior.

Step 3 — Match each to a use case: Use kb.pressing('space') for something that should happen continuously while held, like charging up a jump or flying upward while space is held. Use kb.presses(' ') for a one-time action per press, like triggering a single jump — otherwise holding space would re-trigger the jump every frame.

Answer: pressing is true every frame the key is held (use for continuous actions like moving or flying); presses is true only on the first frame of the press (use for one-shot actions like jumping or shooting).

Problem 3. In the jump example, why do we check player.colliding(ground) before setting player.vel.y = -8? What would happen without this check?

Solution

Step 1 — State the purpose of the check: player.colliding(ground) confirms the player is touching solid ground before allowing a jump.

Step 2 — Explain what happens without it: Every frame you press space — including mid-air — vel.y would be reset to -8, canceling the downward pull from gravity before it accumulates.

Step 3 — Describe the result: The player could jump repeatedly in mid-air (double-jumping, triple-jumping, infinite flight), rising as long as they keep tapping space. The ground check ensures the jump only fires when there's something solid beneath the player.

Answer: The check prevents mid-air jumps. Without it, pressing space repeatedly while airborne would keep resetting vel.y = -8, letting the player fly by canceling gravity's effect every frame.

Problem 4. Write code for a player that moves right when the D key is pressed and left when the A key is pressed. The player should stop when neither key is pressed.

Solution

Step 1 — Plan the logic: Check kb.pressing('d') first, then kb.pressing('a'), using an if/else-if chain so only one direction applies at a time, with an else branch stopping the player.

Step 2 — Write the code:

if (kb.pressing('d'))       player.vel.x = 4;
else if (kb.pressing('a')) player.vel.x = -4;
else                        player.vel.x = 0;

Step 3 — Verify behavior: Pressing D sets velocity right (+4), pressing A sets it left (-4), and releasing both keys hits the else, setting vel.x = 0 so the player stops immediately.

Answer: The if/else-if chain above gives A/D left-right movement with an automatic stop when neither key is held.

Problem 5. What does .vel stand for? What units is it measured in?

Solution

Step 1 — Name the property: .vel stands for velocity.

Step 2 — State its structure and units: It is a vector with .x and .y components describing how fast and in what direction a sprite moves.

Step 3 — Give the unit: Velocity is measured in pixels per frame, so vel.x = 4 means the sprite moves 4 pixels to the right each frame.

Answer: .vel is velocity — a vector measured in pixels per frame, with separate x and y components.

Problem 6. What happens to a sprite's vertical velocity after you set player.vel.y = -8 and the sprite is in the air? Why doesn't it keep going up forever?

Solution

Step 1 — Trace the motion after the assignment: Setting vel.y = -8 launches the sprite upward at 8 pixels per frame (negative y means up on screen).

Step 2 — Bring in gravity: While airborne, world.gravity.y subtracts from vel.y every frame, so the upward speed shrinks: -8, then less negative, then zero at the peak of the arc.

Step 3 — Explain why it doesn't fly forever: Once vel.y reaches 0, gravity keeps working and makes it positive, so the sprite falls back down. Gravity acts continuously, so no constant velocity can hold forever unless you keep re-setting it.

Answer: The sprite rises, slows because gravity reduces vel.y each frame, momentarily stops at the arc's peak, then falls back down. It can't rise forever because gravity continuously opposes the upward velocity.

Problem 7. Modify the jump example so the player jumps higher. What single value would you change?

Solution

Step 1 — Locate the jump code: The jump sets player.vel.y = -8 when space is pressed and the ground check passes.

Step 2 — Identify the control value: The magnitude -8 is exactly what determines jump height — a larger negative number means more upward speed, so gravity takes longer to bring it to zero and the arc peaks higher.

Step 3 — Choose the change: Increase the magnitude, e.g. player.vel.y = -12.

Answer: Change the jump impulse value — make -8 more negative (e.g. -12) so the player launches faster and jumps higher.

Problem 8. What would happen if you set player.vel.y = -8 every frame inside draw (without any key check)? Why?

Solution

Step 1 — Consider what happens each frame: Inside draw, vel.y = -8 runs every single frame, unconditionally.

Step 2 — Compare against gravity: Gravity adds its pull once per frame, but your assignment overwrites vel.y back to -8 every frame, so gravity never gets to accumulate.

Step 3 — Describe the result: The player rises at a constant 8 pixels per frame indefinitely — off the top of the screen and gone — regardless of keys or ground contact.

Answer: The player flies straight up forever at constant speed, because the per-frame assignment keeps resetting vel.y to -8 and gravity never gets a chance to slow it down.

Problem 9. Explain the difference between setting .vel and calling applyForce(), and give one situation that suits each.

Solution

Step 1 — Define setting .vel: Assigning .vel replaces the sprite's current motion — "you are now going this speed," full stop. Physics contributions get overwritten.

Step 2 — Define applyForce(): A force adds a push to whatever motion the sprite already has, and the physics engine computes the resulting movement. Units are Newtons, not pixels per frame.

Step 3 — Match situations: Direct key-driven walking suits .vel — the player should stop instantly when the key releases. A bomb knockback suits applyForce — you want to shove the player away on top of their existing movement, not erase it.

Answer: Setting .vel replaces current motion (good for direct controls like arrow-key walking); applyForce() adds a push to existing motion (good for knockbacks, explosions, or wind), letting physics blend it in.

Problem 10. Why does applyForce(3, 0) barely move a sprite when vel.x = 3 is a brisk walk?

Solution

Step 1 — Compare the units: vel.x = 3 means 3 pixels per frame — a visible, brisk walk. applyForce(3, 0) applies a force of just 3 Newtons.

Step 2 — Explain the mismatch: There is no conversion between pixels-per-frame and Newtons; force values work on a completely different scale, and typical useful forces are in the tens-to-hundreds range (the section's example used 120).

Step 3 — Practical takeaway: Expect to try a number, watch the result, and scale up — often by a factor of ten or more.

Answer: Because applyForce uses Newtons, not pixels per frame — 3 Newtons is a tiny push on that scale, so the barely-visible nudge needs a much larger force value to feel like movement.

Problem 11. In the bomb knockback code, why does the direction of the push depend on comparing player.x and b.x?

Solution

Step 1 — Identify the goal: The knockback must push the player away from the bomb, whichever side they approached from.

Step 2 — Explain the comparison: If player.x < b.x, the player is to the left of the bomb, so pushing them away means pushing further left — the negative x direction. If player.x is greater, the player is to the right, and away means pushing right, the positive x direction.

Step 3 — Connect to the code: That is exactly what const direction = player.x < b.x ? -1 : 1; produces: -1 when the player is on the left, 1 when on the right. Multiplying by 120 in player.applyForce(direction * 120, -60) turns that into a push of the right size in the right direction, and the -60 adds a consistent upward lift on top of it.

Answer: Comparing player.x and b.x tells you which side of the bomb the player is on, so the sign of the horizontal force can be flipped to always push them away from the bomb rather than in one fixed direction. Without the comparison, a player hit from the right would be shoved further into the bomb.

Problem 12. A ball dropped onto a floor lands and does not bounce at all. Nothing is broken — why?

Solution

Step 1 — Recall the defaults: Both bounciness and friction default to 0 in shPlay.

Step 2 — Apply the definition: bounciness = 0 means the sprite keeps none of its speed in a collision — it lands with a thud and stays put.

Step 3 — Conclude: Nothing is broken; the ball simply has the default bounciness of zero. To see a bounce, set ball.bounciness to something between 0 and 1.

Answer: The ball doesn't bounce because bounciness defaults to 0 — sprites keep none of their collision speed unless you explicitly raise bounciness.

Problem 13. Which sprite should carry friction for a golf putt, and which should carry bounciness? Justify each.

Solution

Step 1 — Think about what a putt needs: After being struck, the ball should roll along the green and gradually slow down and stop — that's friction's job.

Step 2 — Assign friction: The green (ground) should carry friction. Giving the surface friction slows anything sliding across it, which is exactly how a real putting green behaves.

Step 3 — Consider bounciness: A golf ball does bounce slightly when it lands, but during a putt the important contact is rolling, not bouncing. If anything, modest bounciness belongs on the ball (it's the object whose rebound character matters), but for a pure putt simulation, friction on the green is the essential property.

Answer: Put friction on the green/ground so the rolling ball slows and stops naturally; bounciness, if modeled at all, belongs on the ball since it describes how the ball itself rebounds on impact.

Problem 14. What are the units of angularVelocity, and what does a negative value do?

Solution

Step 1 — State the units: angularVelocity measures turning speed in degrees per frame.

Step 2 — Interpret positive values: A positive value spins the sprite clockwise (increasing its rotation angle) — e.g., angularVelocity = 3 rotates the sprite 3 degrees clockwise each frame, like the spinning warning signs in §6.3.7.

Step 3 — Interpret negative values: A negative value spins the opposite way — counterclockwise — at the corresponding rate. E.g., -3 rotates 3 degrees counterclockwise per frame.

Answer: angularVelocity is in degrees per frame; negative values spin the sprite counterclockwise instead of clockwise.

Key Terms

Term Definition
Gravity A force that pulls dynamic sprites in a direction; controlled by world.gravity.y
kb.presses A function that returns true only on the first frame a key is pressed
kb.pressing A function that returns true every frame a key is held down
Velocity A vector (x and y components) describing how fast and in what direction a sprite moves, in pixels per frame
Force A push applied with applyForce(fx, fy), measured in Newtons — not pixels per frame. Adds to existing motion rather than replacing it
Bounciness How much speed a sprite keeps after a collision, 0 to 1. Defaults to 0, so nothing bounces unless you say so
Friction How strongly a sprite resists sliding on a surface. Defaults to 0, so sprites slide forever unless you say so
Rotation Which way a sprite faces, in degrees
Angular velocity How fast a sprite spins, in degrees per frame. Negative spins the other way