Learning Goals
3 minBy the end of this lesson you can:
- Move a player actor left and right by checking
keyboard.leftandkeyboard.rightinsideupdate(dt). - Clamp the player at the screen edges so it cannot walk off-screen.
- Use
on_key_down(key)to trigger a one-shot action (for example, printing a message when SPACE is pressed).
Warm-Up · Held vs One-Shot
5 minThink about how a game uses the keyboard. Which actions need a held key and which need a single press?
- Walking left: you hold the key until you stop.
- Jumping: one press, one jump — holding the key should not make you jump forever.
- Firing a bullet: one press fires one bullet.
Pygame Zero gives us two tools for these two situations. Which should you use for each?
Show the answer
- Held →
keyboard.left / right / up / downchecked inupdate().Trueevery frame the key is down. - One-shot →
on_key_down(key). Called exactly once when the key is first pressed, no matter how long you hold it.
New Concept · keyboard and on_key_down
12 minThink of keyboard as a dictionary of switches — each key is either True (held down) or False (released). Checking it in update() means you read it 60 times a second, producing smooth continuous movement.
Held movement in update()
def update(dt): if keyboard.left: player.x -= 200 * dt if keyboard.right: player.x += 200 * dt
Both checks are independent if statements (not elif), so pressing left and right simultaneously cancels out — the player stays still, which feels natural.
Clamping at the edges
Add boundary checks after the movement so the player cannot walk off screen:
if player.x < 0: player.x = 0 if player.x > WIDTH: player.x = WIDTH
Up and down
keyboard.up and keyboard.down work exactly the same way for vertical movement. You can also use letter keys: keyboard.a (left), keyboard.d (right), keyboard.w (up), keyboard.s (down) — common in PC games.
One-shot actions with on_key_down
on_key_down is a hook function — define it and Pygame Zero calls it once per key press:
def on_key_down(key): if key == keys.SPACE: print("Jump!")
keys.SPACE, keys.LEFT, keys.UP are the named constants for special keys. Note: keys (plural) is used for the constant, while keyboard (singular) is for held state.
Why it matters
Player input is what turns a animation into a game. Every game you have ever played uses exactly this pattern: held keys for movement, single-press keys for actions. Mastering it here means every future game you build already has working controls.
Worked Example · Faiz Moves!
12 minThe story
Faiz wants to control his character with the arrow keys, left and right. The character should not be able to walk off the screen. Press SPACE to wave. Save as faiz_moves.py:
# faiz_moves.py — arrow key movement with clamping import pgzrun WIDTH = 600 HEIGHT = 400 TITLE = "Faiz Moves!" player = Actor("alien") player.x = 300 player.y = 340 def draw(): screen.fill("skyblue") player.draw() screen.draw.text( "← → to move SPACE to wave", center=(WIDTH // 2, 20), fontsize=20, color="black", ) def update(dt): if keyboard.left: player.x -= 220 * dt if keyboard.right: player.x += 220 * dt if player.x < 0: player.x = 0 if player.x > WIDTH: player.x = WIDTH def on_key_down(key): if key == keys.SPACE: print("Faiz waves hello!") pgzrun.go()
What you will see
Try holding both arrow keys simultaneously — the player stays still because both movement additions cancel out.
Try It Yourself
13 minExtend faiz_moves.py so keyboard.up and keyboard.down also move the player. Clamp y between 0 and HEIGHT. The player should now move in all four directions.
Hint
def update(dt): if keyboard.left: player.x -= 220 * dt if keyboard.right: player.x += 220 * dt if keyboard.up: player.y -= 220 * dt if keyboard.down: player.y += 220 * dt player.x = max(0, min(WIDTH, player.x)) player.y = max(0, min(HEIGHT, player.y))
Add a global speed = 220. In on_key_down, if SPACE is pressed, triple the speed to 660. In on_key_up, when SPACE is released, reset it back to 220.
Hint
speed = 220 def on_key_down(key): global speed if key == keys.SPACE: speed = 660 def on_key_up(key): global speed if key == keys.SPACE: speed = 220
Mini-Challenge · Kavitha's Broken Controls
8 minKavitha's script is supposed to let the player walk left and right, but pressing left does nothing and pressing right shoots the player off-screen instantly. Find both bugs:
# kavitha_controls.py — buggy
import pgzrun
WIDTH = 600
HEIGHT = 400
hero = Actor("alien")
hero.pos = (300, 350)
SPEED = 250
def draw():
screen.fill("olivedrab")
hero.draw()
def update(dt):
if keyboard.Right:
hero.x += SPEED * dt
if keyboard.LEFT:
hero.x -= SPEED * dt
pgzrun.go()It works if…
holding the left arrow moves the hero left; holding right moves it right; it stays on screen
Show the fix
# kavitha_controls.py — fixed import pgzrun WIDTH = 600 HEIGHT = 400 hero = Actor("alien") hero.pos = (300, 350) SPEED = 250 def draw(): screen.fill("olivedrab") hero.draw() def update(dt): if keyboard.right: # was keyboard.Right (wrong case) hero.x += SPEED * dt if keyboard.left: # was keyboard.LEFT (wrong case) hero.x -= SPEED * dt if hero.x < 0: hero.x = 0 if hero.x > WIDTH: hero.x = WIDTH pgzrun.go()
Two fixes: keyboard.right and keyboard.left must be all lowercase. A missing boundary clamp was also added to stop the hero escaping off-screen.
Recap
3 minCheck keyboard.left / keyboard.right inside update(dt) for smooth held movement. Always clamp the player's position afterwards. For actions that should fire once per press, define on_key_down(key) and compare against keys.SPACE or other constants.
Vocabulary Card
- keyboard.left / right / up / down
- Boolean values —
Truewhile that arrow key is held down. Check inupdate()for continuous movement. - on_key_down(key)
- A hook Pygame Zero calls exactly once when a key is first pressed. Use for jumps, shots, or mode changes.
- keys.SPACE / keys.LEFT
- Named constants for special keys, used inside
on_key_downcomparisons. - Clamping
- Restricting a value to a range:
if x < 0: x = 0ensures the player never goes off the left edge.
Homework
4 minCreate patrol_player.py. Place a player sprite at the bottom of the screen. Use all four arrow keys to move it freely. Clamp on all four edges. Display the player's current x and y position on screen so you can see the clamping in action. Bring a screenshot to the next class.
Stretch. Add a "dash" mechanic: pressing SPACE instantly moves the player 80 px in the direction they last moved (store the last direction as a variable).
Sample · patrol_player.py
# patrol_player.py — four-direction movement with clamping import pgzrun WIDTH = 600 HEIGHT = 400 TITLE = "Patrol Player" player = Actor("alien") player.x = 300 player.y = 350 SPEED = 240 def draw(): screen.fill("darkslategray") player.draw() screen.draw.text( f"x={player.x:.0f} y={player.y:.0f}", topleft=(10, 10), fontsize=22, color="white", ) def update(dt): if keyboard.left: player.x -= SPEED * dt if keyboard.right: player.x += SPEED * dt if keyboard.up: player.y -= SPEED * dt if keyboard.down: player.y += SPEED * dt if player.x < 0: player.x = 0 if player.x > WIDTH: player.x = WIDTH if player.y < 0: player.y = 0 if player.y > HEIGHT: player.y = HEIGHT pgzrun.go()
The key pattern is four independent if keyboard.* checks followed by four clamp checks. Background and speed are your own.