What you will learn
This chapter is all about handling user input. You will discover the crucial difference between events (a one-time key press) and key states (a key being held down). You will also learn how to use the mouse as an alternative control method. By the end, your paddle will finally move!
The concept
Pygame offers two approaches for reading the keyboard:
- Events (
pygame.event.get()): they fire once when a key is pressed or released. Ideal for discrete actions: opening a menu, pausing, switching modes. - Key states (
pygame.key.get_pressed()): returns a dictionary of booleans indicating which keys are currently held down. Ideal for continuous movement: moving a character, a paddle.
The golden rule: use events for one-shot actions, and key states for continuous actions.
For the mouse, pygame.mouse.get_pos() returns the current cursor position as a tuple (x, y). It is perfect for direct, fluid paddle control.
Step by step
1. Event for mode switching
We use a KEYDOWN event to toggle between keyboard and mouse mode with the M key. This is a one-shot action: press once, mode changes.
use_mouse = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if event.key == pygame.K_m:
use_mouse = not use_mouse
If we used get_pressed() for this action, the mode would toggle 60 times per second while M is held, which would be unusable.
2. Key states for movement
For paddle movement, we read key states every frame. As long as the left arrow is held, the paddle moves left. We also support A and D keys for players used to WASD controls.
if use_mouse:
mouse_x, _ = pygame.mouse.get_pos()
paddle_x = mouse_x - PADDLE_WIDTH // 2
else:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
paddle_x -= PADDLE_SPEED
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
paddle_x += PADDLE_SPEED
In mouse mode, we center the paddle on the cursor by subtracting half its width. In keyboard mode, we move the paddle by PADDLE_SPEED pixels per frame.
3. Clamping the paddle to the screen
Without constraints, the paddle could go off-screen. We use max() and min() to keep it within bounds. This is called clamping.
# Clamp paddle to screen
paddle_x = max(0, min(paddle_x, SCREEN_WIDTH - PADDLE_WIDTH))
This line guarantees that paddle_x will never be less than 0 (left edge) or greater than SCREEN_WIDTH - PADDLE_WIDTH (right edge). We subtract PADDLE_WIDTH because paddle_x represents the left edge of the paddle.
4. Displaying the active mode
We display the current control mode and available instructions at the bottom of the screen, so the player always knows how to interact.
mode = "MOUSE" if use_mouse else "KEYBOARD"
info = font.render(
f"Mode: {mode} [M] Switch [Arrows/AD] Move [ESC] Quit",
True, (150, 150, 150)
)
screen.blit(info, (10, SCREEN_HEIGHT - 25))
Key takeaways
Tip: you can combine both approaches. For example, use a KEYDOWN event to fire a projectile (once per press) and get_pressed() for continuous ship movement.
Common pitfall: be careful, K_LEFT is not the same as K_a. The first is an arrow key, the second is the letter A. On some keyboard layouts (AZERTY), you may need to handle K_q instead of K_a.
Common pitfall: remember that PADDLE_SPEED = 8 means 8 pixels per frame. At 60 FPS, that is 480 pixels per second. Adjust this value for comfortable movement.
Pygame functions used:
pygame.key.get_pressed(): state of all keys (True/False)pygame.mouse.get_pos(): cursor position (x, y)pygame.K_LEFT,K_RIGHT, etc.: key constants
Complete code
"""Brick Breaker - Chapter 4: Handling Input"""
import pygame
import sys
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
BG_COLOR = (20, 20, 40)
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
PADDLE_COLOR = (200, 200, 200)
PADDLE_SPEED = 8
paddle_x = SCREEN_WIDTH // 2 - PADDLE_WIDTH // 2
paddle_y = SCREEN_HEIGHT - 40
BALL_RADIUS = 8
BALL_COLOR = (255, 255, 255)
ball_x = SCREEN_WIDTH // 2
ball_y = SCREEN_HEIGHT // 2
BRICK_ROWS = 6
BRICK_COLS = 10
BRICK_WIDTH = 70
BRICK_HEIGHT = 20
BRICK_PADDING = 5
BRICK_OFFSET_X = 30
BRICK_OFFSET_Y = 50
def rainbow_color(row, total_rows):
hue = int(360 * row / total_rows)
color = pygame.Color(0)
color.hsva = (hue, 100, 100, 100)
return (color.r, color.g, color.b)
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Brick Breaker - Chapter 4")
clock = pygame.time.Clock()
font = pygame.font.SysFont("monospace", 14)
use_mouse = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if event.key == pygame.K_m:
use_mouse = not use_mouse
# Input: key states (continuous) vs events (discrete)
if use_mouse:
mouse_x, _ = pygame.mouse.get_pos()
paddle_x = mouse_x - PADDLE_WIDTH // 2
else:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
paddle_x -= PADDLE_SPEED
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
paddle_x += PADDLE_SPEED
# Clamp paddle to screen
paddle_x = max(0, min(paddle_x, SCREEN_WIDTH - PADDLE_WIDTH))
screen.fill(BG_COLOR)
# Draw bricks
for row in range(BRICK_ROWS):
for col in range(BRICK_COLS):
x = BRICK_OFFSET_X + col * (BRICK_WIDTH + BRICK_PADDING)
y = BRICK_OFFSET_Y + row * (BRICK_HEIGHT + BRICK_PADDING)
color = rainbow_color(row, BRICK_ROWS)
if (row + col) % 2 == 0:
color = tuple(min(255, c + 30) for c in color)
pygame.draw.rect(screen, color, (x, y, BRICK_WIDTH, BRICK_HEIGHT))
pygame.draw.rect(screen, (255, 255, 255),
(x, y, BRICK_WIDTH, BRICK_HEIGHT), 1)
# Draw paddle
pygame.draw.rect(screen, PADDLE_COLOR,
(paddle_x, paddle_y, PADDLE_WIDTH, PADDLE_HEIGHT))
# Draw ball
pygame.draw.circle(screen, BALL_COLOR, (ball_x, ball_y), BALL_RADIUS)
# Instructions
mode = "MOUSE" if use_mouse else "KEYBOARD"
info = font.render(
f"Mode: {mode} [M] Switch [Arrows/AD] Move [ESC] Quit",
True, (150, 150, 150)
)
screen.blit(info, (10, SCREEN_HEIGHT - 25))
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()
Download
Download the source code for this chapter: brick-breaker-ch04.zip
