Collision in code
Use pygame.Rect and its colliderect method to detect overlaps between the player and obstacles, then respond to them.
- Create and position pygame.Rect objects
- Use colliderect to test whether two rects overlap
- Respond to a collision by resetting or blocking movement
- Draw debug outlines to visualise hitboxes
pygame.Rect is pygame's workhorse for both drawing and collision. You have
been using it implicitly in every pygame.draw.rect call. This lesson makes
it explicit and shows you how to use it for detection.
pygame.Rect basics
A Rect stores position and size and exposes useful attributes:
r = pygame.Rect(100, 80, 50, 40) # (x, y, width, height)
r.left # 100 — left edge x
r.right # 150 — right edge x (left + width)
r.top # 80 — top edge y
r.bottom # 120 — bottom edge y (top + height)
r.centerx # 125 — horizontal centre
r.centery # 100 — vertical centre
r.width # 50
r.height # 40You can move a rect by assigning to position attributes:
r.x += 5 # shift right
r.topleft = (200, 100) # teleportRects are mutable objects — changing an attribute changes the underlying values immediately.
Detecting overlaps
.colliderect(other) returns True if the two rects overlap, False
otherwise. It implements exactly the AABB check described in the previous
lesson.
player = pygame.Rect(100, 100, 40, 40)
wall = pygame.Rect(120, 110, 60, 60)
if player.colliderect(wall):
print("collision!")That's the whole API for basic collision. The complexity lives in how you respond to the collision, not in detecting it.
Collision response — push back
The simplest response to hitting a wall is to undo the move that caused the collision. Store the previous position before updating, and restore it if a collision occurs.
Pyodide (the in-browser Python runner) does not support pygame's display
system. Read through this code, then run it locally:
pip install pygame followed by python collision.py.
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Collision")
clock = pygame.time.Clock()
BG = (20, 20, 30)
PLAYER_C = (100, 210, 120)
WALL_C = (180, 80, 80)
DEBUG_C = (255, 255, 0) # yellow outline for hitboxes
SPEED = 4
player = pygame.Rect(60, 200, 40, 40)
# A list of wall rects the player cannot pass through
walls = [
pygame.Rect(200, 100, 20, 280), # vertical wall
pygame.Rect(350, 200, 200, 20), # horizontal wall
]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Save position before moving
old_x, old_y = player.x, player.y
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]: player.x -= SPEED
if keys[pygame.K_RIGHT]: player.x += SPEED
if keys[pygame.K_UP]: player.y -= SPEED
if keys[pygame.K_DOWN]: player.y += SPEED
# Clamp inside window
player.clamp_ip(screen.get_rect())
# Check each wall; restore position if the player overlaps
for wall in walls:
if player.colliderect(wall):
player.x, player.y = old_x, old_y
break
# Render
screen.fill(BG)
for wall in walls:
pygame.draw.rect(screen, WALL_C, wall)
pygame.draw.rect(screen, PLAYER_C, player)
# Debug: draw hitbox outlines (comment out when satisfied)
pygame.draw.rect(screen, DEBUG_C, player, 1)
for wall in walls:
pygame.draw.rect(screen, DEBUG_C, wall, 1)
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()What each part does
old_x, old_y = player.x, player.y snapshots the position before any
movement. If a collision fires, these values restore the player to where they
were before the offending move.
player.clamp_ip(screen.get_rect()) is a built-in rect method that moves
the rect inward until it fits inside the argument rect. _ip means "in place"
— it modifies the rect directly. This replaces the manual min/max clamping from
the earlier lessons.
player.colliderect(wall) is the AABB test. If it returns True, we
immediately undo the player's last move with the snapshot values. The break
exits the wall loop early — once a collision is found, there's no need to check
the rest.
pygame.draw.rect(screen, colour, rect, 1) draws an outline (width=1)
rather than a filled rectangle. This makes hitboxes visible during development
without hiding the sprites underneath. Remove or comment out debug drawing
before releasing.
The push-back approach is simple and works well for rectangular obstacles. For smoother collision response — sliding along walls instead of stopping — you would separate the x and y move into two steps and resolve each axis independently. That is a natural next step once the basics are solid.
Where to go next
Next: lab — polish — add obstacles, a score counter, and a restart mechanic to the basic game you built in the previous module.
Collision concepts
Collision detection asks whether two objects overlap. Axis-aligned bounding boxes are the fast, practical answer for most 2D games.
Lab: Polish the basic game
Extend your basic game with obstacles that reset progress on collision, a score that increments on reaching the target, and an R-key restart.