Editor

Rectangles and collisions

Pygame fundamentals · Lesson 4 of 7

A pygame.Rect holds a position and a size, and it can tell you when two things touch.

A Rect is a box you can move

pygame.Rect(x, y, width, height) remembers a box. Change rect.x and rect.y to move it, and read its edges and middle by name: rect.left, rect.right, rect.top, rect.bottom, rect.center. You can also set them: rect.center = (240, 160) moves the box so its middle is there.

Bouncing is a change of direction: the ball has a speed across (dx) and a speed down (dy), and hitting a wall turns one of them around.

import pygame

pygame.init()
screen = pygame.display.set_mode((480, 320))
clock = pygame.time.Clock()
ball = pygame.Rect(100, 80, 16, 16)
dx, dy = 4, 3

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    ball.x += dx
    ball.y += dy
    if ball.left < 0 or ball.right > 480:
        dx = -dx
    if ball.top < 0 or ball.bottom > 320:
        dy = -dy

    screen.fill((16, 18, 22))
    pygame.draw.ellipse(screen, (255, 212, 59), ball)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Touching: colliderect

a.colliderect(b) is True when two rectangles overlap. Here the ball bounces off the paddle only while it is moving down (dy > 0), so it cannot get stuck inside the paddle and bounce back and forth.

paddle.clamp_ip(screen.get_rect()) keeps the paddle inside the window, a shorter way to write the max and min from the last lesson.

import pygame

pygame.init()
screen = pygame.display.set_mode((480, 320))
clock = pygame.time.Clock()
font = pygame.font.Font(None, 30)
ball = pygame.Rect(240, 60, 14, 14)
paddle = pygame.Rect(200, 290, 80, 12)
dx, dy = 3, 4
bounces = 0

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        paddle.x -= 6
    if keys[pygame.K_RIGHT]:
        paddle.x += 6
    paddle.clamp_ip(screen.get_rect())

    ball.x += dx
    ball.y += dy
    if ball.left < 0 or ball.right > 480:
        dx = -dx
    if ball.top < 0:
        dy = -dy
    if dy > 0 and ball.colliderect(paddle):
        dy = -dy
        bounces += 1
    if ball.top > 320:  # missed: serve again from the top
        ball.topleft = (240, 60)
        bounces = 0

    screen.fill((16, 18, 22))
    pygame.draw.rect(screen, (255, 212, 59), paddle, border_radius=5)
    pygame.draw.ellipse(screen, (230, 232, 235), ball)
    screen.blit(font.render(f"Bounces {bounces}", True, (230, 232, 235)), (12, 10))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

The mouse: collidepoint

A MOUSEBUTTONDOWN event carries event.pos, the point that was clicked. rect.collidepoint(point) says whether that point is inside the rectangle, which is all a clickable button or target needs.

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((480, 320))
clock = pygame.time.Clock()
font = pygame.font.Font(None, 30)
target = pygame.Rect(200, 120, 60, 60)
hits = 0

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN and target.collidepoint(event.pos):
            hits += 1
            target.topleft = (random.randint(0, 420), random.randint(40, 260))

    screen.fill((16, 18, 22))
    pygame.draw.ellipse(screen, (235, 95, 85), target)
    pygame.draw.ellipse(screen, (255, 255, 255), target.inflate(-30, -30))
    screen.blit(font.render(f"Hits {hits}", True, (230, 232, 235)), (12, 10))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Your turn

Change the programs above in the game panel, then press Run.

  • Make the ball a little faster every time it bounces off the paddle.
  • Shrink the target by a few pixels with every hit (target.inflate_ip(-4, -4)).
  • Add a second ball with its own dx and dy.

Breakout is this lesson grown up: a ball, a paddle and sixty Rects to hit. Open Breakout.