Learning Goals
3 minBy the end of this lesson you can:
- Check multiple
keyboard.*booleans insideupdate()to handle simultaneous key presses. - Move a sprite diagonally by combining horizontal and vertical checks with
if/elif/ independent conditions. - Run a working script where an actor responds to eight-directional keyboard input.
Warm-Up · One Key at a Time
5 minIn PZ-13 you moved a sprite with arrow keys one direction at a time. Here is a mini version of that logic. What is printed if the player holds the right arrow?
# Imagine keyboard.right = True, keyboard.up = False keyboard_right = True keyboard_up = False if keyboard_right: print("moving right") if keyboard_up: print("moving up")
Show the answer
Output
moving right
Only one line prints because keyboard_up is False. But if both were True, both lines would print — that is the key insight for today.
New Concept · Checking Several Keys Together
12 minThink of your keyboard like a panel of light switches. Every switch is either on (True) or off (False) at this exact moment. You can read as many switches as you like in the same update() call — they do not interfere with each other.
How keyboard.* works
keyboard.right, keyboard.up, keyboard.left, keyboard.down and keyboard.space are all just Python booleans. Read them with a plain if:
def update(): if keyboard.right: player.x += 4 if keyboard.left: player.x -= 4 if keyboard.up: player.y -= 4 if keyboard.down: player.y += 4
Notice these are four separate if blocks, not elif. That means when keyboard.right and keyboard.up are both True, both branches run — and the sprite moves diagonally.
Diagonal combos with and
You can also check a combo directly — useful for special moves or boosted speed:
def update(): if keyboard.up and keyboard.right: player.x += 6 # faster diagonal boost player.y -= 6 elif keyboard.up: player.y -= 4 elif keyboard.right: player.x += 4
Here the and combo is tested first. If neither key is held, the plain branches handle single directions.
Why it matters
Every real game uses multi-key detection. A side-scroller needs jump+shoot. A racing game needs steer+accelerate. Getting comfortable with independent if blocks is the foundation of all of it.
Worked Example · Eight-Direction Player
12 minThe story
Faiz is building a top-down game. He wants his character to move smoothly in all eight directions using the arrow keys. Save this as eight_dir.py:
# eight_dir.py — eight-direction movement import pgzrun WIDTH = 600 HEIGHT = 400 TITLE = "Eight Directions" SPEED = 4 player = Actor("alien") # images/alien.png or coloured circle fallback player.pos = (300, 200) def draw(): screen.fill("darkslateblue") player.draw() screen.draw.text( "Arrow keys — try two at once!", topleft=(10, 10), fontsize=22, color="white", ) def update(): if keyboard.right: player.x += SPEED if keyboard.left: player.x -= SPEED if keyboard.up: player.y -= SPEED if keyboard.down: player.y += SPEED # keep player inside the window player.x = max(20, min(WIDTH - 20, player.x)) player.y = max(20, min(HEIGHT - 20, player.y)) pgzrun.go()
What you'll see
If you do not have images/alien.png, replace Actor("alien") with a drawn circle. In draw() swap player.draw() for screen.draw.filled_circle((int(player.x), int(player.y)), 18, "lime"). Update player.x and player.y as normal — they are plain numbers on any Actor.
Try It Yourself
13 minChange SPEED to 7 and run the script. Then try 2. Notice how the constant at the top controls everything — you only change one line.
Hint
SPEED = 7 # change this one value only
Add a check at the top of update(): if the player holds both keyboard.up and keyboard.right at the same time, move by SPEED + 3 in both axes instead of the normal speed. Use an and combo as a first if.
Hint
def update(): if keyboard.up and keyboard.right: player.x += SPEED + 3 player.y -= SPEED + 3 else: if keyboard.right: player.x += SPEED if keyboard.up: player.y -= SPEED
Mini-Challenge 🔥 · Debug: Aiman's Stuck Sprite
8 minAiman wrote a script so his sprite sprints when he holds keyboard.space at the same time as an arrow key. The sprite moves, but the sprint never activates. Find and fix the bug.
# aiman_sprint.py — buggy
import pgzrun
WIDTH = 600
HEIGHT = 400
SPEED = 4
player = Actor("alien")
player.pos = (300, 200)
def draw():
screen.fill("navy")
player.draw()
def update():
speed = SPEED
if keyboard.space and keyboard.right:
speed = 10
elif keyboard.right:
player.x += speed # bug is here — what is wrong?
elif keyboard.left:
player.x -= speed
pgzrun.go()It works if…
holding SPACE + right arrow moves the player noticeably faster
Show the fix
# aiman_sprint.py — fixed import pgzrun WIDTH = 600 HEIGHT = 400 SPEED = 4 player = Actor("alien") player.pos = (300, 200) def draw(): screen.fill("navy") player.draw() def update(): speed = SPEED if keyboard.space: speed = 10 # boost speed when SPACE is held if keyboard.right: player.x += speed # now uses the boosted speed too if keyboard.left: player.x -= speed pgzrun.go()
The fix: set speed based on keyboard.space alone first, then apply it in separate direction checks. The original elif chain meant the right-movement branch never ran when the sprint combo was active.
Recap
3 minEvery keyboard.* attribute is a boolean that is True while the key is held. Using four separate if blocks — one per direction — lets multiple keys stack naturally and gives you eight-directional movement for free. Use and combos to detect two-key specials like a sprint or a dash.
Vocabulary Card
- keyboard.*
- A set of boolean attributes (
keyboard.left,keyboard.space, …) that areTruewhile that key is held down. - separate if blocks
- Independent conditions checked every frame — both run if both are true, enabling diagonal movement.
- key combo (and)
- A condition like
keyboard.up and keyboard.rightthat is only true when both keys are held at the same time. - SPEED constant
- A module-level variable written in CAPITALS that controls movement speed from one central place.
Homework
4 minStart from the eight_dir.py example. Add WASD controls alongside the arrow keys — holding W should also move up, A should move left, S should move down, and D should move right. Save as eight_dir_wasd.py and bring a screenshot next class.
Hint: keyboard.w, keyboard.a, keyboard.s, keyboard.d work the same way as the arrow keys.
Sample · eight_dir_wasd.py
# eight_dir_wasd.py — arrows + WASD import pgzrun WIDTH = 600 HEIGHT = 400 SPEED = 4 player = Actor("alien") player.pos = (300, 200) def draw(): screen.fill("darkslateblue") player.draw() def update(): if keyboard.right or keyboard.d: player.x += SPEED if keyboard.left or keyboard.a: player.x -= SPEED if keyboard.up or keyboard.w: player.y -= SPEED if keyboard.down or keyboard.s: player.y += SPEED player.x = max(20, min(WIDTH - 20, player.x)) player.y = max(20, min(HEIGHT - 20, player.y)) pgzrun.go()
The key trick is the or — both input schemes share the same movement block. Your sprite image and speed may differ.