Learning Goals
3 minBy the end of this lesson you can:
- Implement gravity by adding
GRAVITYtovyevery frame and moving the actor byvy. - Land an actor on a floor by checking
actor.bottom >= FLOOR_Yand resettingvy = 0. - Make the actor jump by setting
vyto a negative impulse value when the player presses a key — and only when the actor is on the ground.
Warm-Up · Which Way Is Down?
5 minIn Pygame Zero, (0, 0) is the top-left corner and y grows downward. Predict what happens to an actor with these values after two frames:
# start of frame 0 y = 100 vy = 5 # each frame: vy = vy + 1 y = y + vy
Show the answer
Trace
Frame 0: vy = 5+1 = 6, y = 100+6 = 106 Frame 1: vy = 6+1 = 7, y = 106+7 = 113
The actor moves down the screen (y increases) and gets faster each frame — exactly like a falling object. That +1 each frame is gravity.
New Concept · Gravity, Landing, and Jumping
12 minThink of vy as the actor's speed in the vertical direction — positive means moving down. Gravity is a constant pull that adds to vy every single frame, making the actor fall faster and faster until something stops it.
Gravity — three lines
GRAVITY = 0.5 def update(): global vy vy += GRAVITY # gravity pulls down (increases vy) player.y += vy # move the actor
Landing on the floor
When the bottom of the actor touches the floor, snap it to the floor surface and zero the velocity:
FLOOR_Y = 500 # y-coordinate of the floor surface def update(): global vy, on_ground vy += GRAVITY player.y += vy if player.bottom >= FLOOR_Y: player.bottom = FLOOR_Y vy = 0 on_ground = True else: on_ground = False
Jumping
A jump sets vy to a negative value — that pushes the actor upward (remember y grows down, so negative vy means moving up). Only allow a jump when the actor is already on the ground:
JUMP_FORCE = -12 # negative = upward def on_key_down(key): global vy if key == keys.SPACE and on_ground: vy = JUMP_FORCE
Worked Example · A Jumping Ball
12 minAisyah wants to test gravity before building a full platform game. She draws a coloured circle (no image needed) that falls, lands on a floor line, and jumps when she presses SPACE. Save as jump_test.py:
# jump_test.py — gravity and jump demo import pgzrun WIDTH = 480 HEIGHT = 400 TITLE = "Gravity Demo" GRAVITY = 0.5 JUMP_FORCE = -13 FLOOR_Y = 360
player_x = 240 player_y = 200 vy = 0.0 on_ground = False def update(): global player_y, vy, on_ground vy += GRAVITY player_y += vy if player_y + 20 >= FLOOR_Y: player_y = FLOOR_Y - 20 vy = 0.0 on_ground = True else: on_ground = False def on_key_down(key): global vy if key == keys.SPACE and on_ground: vy = JUMP_FORCE def draw(): screen.fill("skyblue") screen.draw.filled_rect( Rect((0, FLOOR_Y), (WIDTH, HEIGHT - FLOOR_Y)), "saddlebrown" ) screen.draw.filled_circle((int(player_x), int(player_y)), 20, "red") hint = "On ground — SPACE to jump" if on_ground else "In the air" screen.draw.text(hint, topleft=(10, 10), fontsize=22, color="white") pgzrun.go()
Press SPACE to launch the ball. Watch how it rises slowly, pauses at the top, then falls faster — that arc is gravity doing its job.
Try It Yourself
13 minChange GRAVITY to 0.3 and JUMP_FORCE to -10. Then try GRAVITY = 1.5 and JUMP_FORCE = -20. Describe in one sentence how the feel changes.
# just edit the two constants at the top GRAVITY = 0.3 JUMP_FORCE = -10
Add left/right movement with LEFT and RIGHT keys. Keep the player inside the window by clamping player_x between 20 and WIDTH - 20.
WALK_SPEED = 4 def update(): global player_x if keyboard.left: player_x -= WALK_SPEED if keyboard.right: player_x += WALK_SPEED player_x = max(20, min(WIDTH - 20, player_x))
Allow one mid-air jump (a "double jump") by tracking how many jumps have been used since last landing. Reset the counter to 0 on landing, allow jumps while the counter is below 2.
Mini-Challenge · Debug Kavitha's Jump
8 minKavitha's character never stops falling — it sinks through the floor. Find the two bugs.
# kavitha_jump.py — buggy
GRAVITY = 0.5
FLOOR_Y = 360
player_y = 100
vy = 0.0
on_ground = False
def update():
global player_y, vy, on_ground
player_y += vy
vy += GRAVITY
if player_y >= FLOOR_Y:
vy = 0.0
on_ground = True
else:
on_ground = FalseIt works if…
the circle rests on the floor (doesn't sink), and on_ground is True while standing
Show the fixed version
# kavitha_jump.py — fixed GRAVITY = 0.5 FLOOR_Y = 360 player_y = 100 vy = 0.0 on_ground = False def update(): global player_y, vy, on_ground # Bug 1: gravity must be added BEFORE moving, not after vy += GRAVITY player_y += vy # Bug 2: player_y must be clamped to FLOOR_Y so it doesn't sink through if player_y + 20 >= FLOOR_Y: player_y = FLOOR_Y - 20 vy = 0.0 on_ground = True else: on_ground = False
Two bugs: gravity was added after moving (late update gives wrong arc), and the floor check forgot to snap player_y back so the actor kept sinking.
Recap
3 minGravity is just vy += GRAVITY every frame. The actor falls because each frame its vertical speed increases. Landing resets vy to zero and snaps the actor to the floor surface. Jumping sets vy to a negative impulse — upward in screen coordinates — and gravity pulls it back down automatically. Remember: y grows downward on screen, so upward motion means a smaller y value.
Vocabulary Card
- vy (vertical velocity)
- How many pixels the actor moves downward per frame. Positive = falling; negative = rising.
- GRAVITY
- A small positive constant added to
vyevery frame, making the actor accelerate downward. - JUMP_FORCE
- A negative value assigned to
vywhen the player jumps — it launches the actor upward. - on_ground
- A boolean flag that is
Trueonly when the actor is touching the floor. Guards against infinite jumping.
Homework
4 minAdd a ceiling to the jump demo. When the top of the actor reaches y = 0 (the top edge of the window), immediately reverse vy to a small positive value (e.g. vy = 2) so the actor bounces off the ceiling instead of leaving the screen. Save as jump_ceiling.py.
Sample · jump_ceiling.py
# jump_ceiling.py — ceiling bounce added import pgzrun WIDTH = 480 HEIGHT = 400 GRAVITY = 0.5 JUMP_FORCE = -13 FLOOR_Y = 360 RADIUS = 20 player_x = 240 player_y = 200 vy = 0.0 on_ground = False def update(): global player_y, vy, on_ground vy += GRAVITY player_y += vy # floor landing if player_y + RADIUS >= FLOOR_Y: player_y = FLOOR_Y - RADIUS vy = 0.0 on_ground = True else: on_ground = False # ceiling bounce if player_y - RADIUS <= 0: player_y = RADIUS vy = 2 def on_key_down(key): global vy if key == keys.SPACE and on_ground: vy = JUMP_FORCE def draw(): screen.fill("skyblue") screen.draw.filled_rect(Rect((0, FLOOR_Y), (WIDTH, HEIGHT - FLOOR_Y)), "saddlebrown") screen.draw.filled_circle((int(player_x), int(player_y)), RADIUS, "red") pgzrun.go()
The ceiling check mirrors the floor check — clamp position, set a small positive vy to bounce away.