33. Events and input
A game that ignores the keyboard and mouse is just a screensaver. This chapter covers how pygame collects input and how to act on it.
The event queue
Every time something happens — a key press, a mouse click, the user
closing the window — pygame stores it in an internal event
queue. At the start of each frame,
pygame.event.get() drains that queue and returns a list of
Event objects. You loop through the list and check each
event's .type attribute:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = FalseIf you never call pygame.event.get(), the queue fills up
and the window stops responding to the X button.
Keyboard events: KEYDOWN and KEYUP
pygame.KEYDOWN fires once when a key is
pressed down. pygame.KEYUP fires once when
a key is released.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
print(f"key pressed: {event.key}")
if event.type == pygame.KEYUP:
print(f"key released: {event.key}")event.key is an integer constant. pygame provides named
constants for every key:
| Constant | Key |
|---|---|
pygame.K_LEFT |
Left arrow |
pygame.K_RIGHT |
Right arrow |
pygame.K_UP |
Up arrow |
pygame.K_DOWN |
Down arrow |
pygame.K_SPACE |
Spacebar |
pygame.K_RETURN |
Enter |
pygame.K_ESCAPE |
Escape |
pygame.K_a |
A key |
pygame.K_z |
Z key |
pygame.K_0 |
Number 0 |
pygame.K_9 |
Number 9 |
Letter constants are lowercase: pygame.K_a through
pygame.K_z.
KEYDOWN fires once per press. It does
not repeat while the key is held. This makes it good for one-shot
actions like jumping or toggling a menu:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
jump() # called once, not 60 times per second
if event.key == pygame.K_ESCAPE:
running = FalseOpen exercises/33/01-print-key.py. In the event loop,
add a KEYDOWN handler that prints
f"pressed: {pygame.key.name(event.key)}".
pygame.key.name(key) converts a key constant to a
human-readable string like "left" or "space".
Run the program and press several keys.
Continuous input:
pygame.key.get_pressed()
KEYDOWN is awkward for movement because it fires only
once. If you want a character to keep moving while the player holds a
key, use pygame.key.get_pressed():
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= 5
if keys[pygame.K_RIGHT]:
x += 5pygame.key.get_pressed() returns a sequence where each
position is True if that key is currently held and
False otherwise. Call it once per frame in the
update state section of the loop (not inside the event
loop). Because it is checked every frame, movement is smooth.
KEYDOWN vs get_pressed — when to use each.
KEYDOWN: one-shot actions that should happen exactly once per press (jump, fire, toggle).get_pressed: continuous actions that should repeat every frame the key is held (walk left, accelerate, scroll).
Mouse events
pygame.MOUSEBUTTONDOWN fires when a mouse button is
pressed.
if event.type == pygame.MOUSEBUTTONDOWN:
print(f"click at {event.pos}, button {event.button}")event.pos—(x, y)tuple of the cursor position at the time of the click.event.button— which button:1= left,2= middle scroll wheel,3= right.
pygame.MOUSEBUTTONUP fires when a button is released
(same attributes).
To get the current mouse position at any time (not just on click):
mx, my = pygame.mouse.get_pos()This is useful for drawing something that follows the cursor:
# In the draw section:
mx, my = pygame.mouse.get_pos()
pygame.draw.circle(screen, (255, 255, 0), (mx, my), 10)Putting input together
Here is a program that moves a square with the arrow keys, changes its colour on spacebar, and prints the mouse position on left-click:
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Input demo")
clock = pygame.time.Clock()
x, y = 370, 270
size = 60
color = (255, 50, 50)
running = True
while running:
# 1. Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
color = (50, 50, 255) # turn blue on spacebar
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
color = (255, 50, 50) # back to red when released
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
print(f"clicked at {event.pos}")
# 2. Update
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= 4
if keys[pygame.K_RIGHT]:
x += 4
if keys[pygame.K_UP]:
y -= 4
if keys[pygame.K_DOWN]:
y += 4
# 3. Draw
screen.fill((30, 30, 30))
pygame.draw.rect(screen, color, (x, y, size, size))
# 4. Flip
pygame.display.flip()
clock.tick(60)
pygame.quit()Homework
Problem 1 — Key reporter
Open exercises/33/homework/01-key-reporter.py. Show
which key is currently held on screen. Use
pygame.key.get_pressed() to detect held keys. When no key
is held, show "no key". Display the text as a printed
message to the terminal each frame (text rendering comes in chapter
35).
Problem 2 — Click counter
Open exercises/33/homework/02-click-counter.py. Count
how many times the user left-clicks in the window. Print the count to
the terminal each time it increases.
Problem 3 — Arrow mover
Open exercises/33/homework/03-arrow-mover.py. A circle
starts at the centre. Arrow keys move it continuously
(get_pressed). Pressing Escape quits the program. Print
"moved" to the terminal each frame the circle is
moving.
Challenge — Mouse follower
Open exercises/33/homework/04-mouse-follower.py. Draw a
small circle at the current mouse position every frame so it follows the
cursor. When the left mouse button is held (check
pygame.MOUSEBUTTONDOWN and MOUSEBUTTONUP to
track a held state), change the circle's colour.
Stuck or finished? Open the homework solutions page.