What you will learn
In this chapter, you will learn to draw geometric shapes on the screen: rectangles, circles, and lines. These are the building blocks (pun intended) of any 2D game. By the end, you will have a paddle, a ball, and a wall of colored bricks displayed on screen.
The concept
Pygame provides a pygame.draw module that contains functions to draw directly onto a Surface. The game window is itself a Surface. At each frame, we erase everything with fill() then redraw each element.
The main functions are:
pygame.draw.rect(surface, color, rectangle): draws a rectanglepygame.draw.circle(surface, color, center, radius): draws a circlepygame.draw.line(surface, color, start, end): draws a line
A rectangle is defined by a tuple (x, y, width, height). The (x, y) coordinates represent the top-left corner. Colors are RGB tuples from 0 to 255.
The order in which you draw matters: whatever is drawn last appears on top of everything else. This is the painter's algorithm. Draw the background first, then the bricks, then the paddle, then the ball.
Step by step
1. Defining element dimensions
We start by defining constants for each game element. Using uppercase constants makes the code readable and easy to tweak.
# Paddle
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
PADDLE_COLOR = (200, 200, 200)
paddle_x = SCREEN_WIDTH // 2 - PADDLE_WIDTH // 2
paddle_y = SCREEN_HEIGHT - 40
# Ball
BALL_RADIUS = 8
BALL_COLOR = (255, 255, 255)
ball_x = SCREEN_WIDTH // 2
ball_y = SCREEN_HEIGHT // 2
# Bricks
BRICK_ROWS = 5
BRICK_COLS = 10
BRICK_WIDTH = 70
BRICK_HEIGHT = 20
BRICK_PADDING = 5
BRICK_OFFSET_X = 30
BRICK_OFFSET_Y = 50
Notice that paddle_x is calculated to center the paddle horizontally. The // operator performs integer division, which matters because pixels are integers.
2. Color palette for bricks
Each row of bricks gets a different color. We store the colors in a list, indexed by row number.
BRICK_COLORS = [
(231, 76, 60), # Red
(230, 126, 34), # Orange
(241, 196, 15), # Yellow
(46, 204, 113), # Green
(52, 152, 219), # Blue
]
3. Drawing bricks with a nested loop
We use two nested loops: one for rows, one for columns. Each brick's position is calculated from its row and column, accounting for spacing (BRICK_PADDING) and the initial offset.
# 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 = BRICK_COLORS[row]
pygame.draw.rect(screen, color, (x, y, BRICK_WIDTH, BRICK_HEIGHT))
4. Drawing the paddle and ball
The paddle is a simple rectangle. The ball is a circle. Note that draw.circle takes the center of the circle, while draw.rect takes the top-left corner.
# 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)
Key takeaways
Tip: to draw only the outline of a rectangle (no fill), add a width parameter: pygame.draw.rect(screen, color, rect, 2) draws a 2-pixel-thick outline.
Common pitfall: if your bricks seem misaligned, check that you are not forgetting the padding in the position calculation. A classic mistake is writing col * BRICK_WIDTH instead of col * (BRICK_WIDTH + BRICK_PADDING).
Pygame functions used:
pygame.draw.rect(): draws a rectangle (filled or outline)pygame.draw.circle(): draws a circle
Complete code
"""Brick Breaker - Chapter 2: Drawing Primitives"""
import pygame
import sys
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
BG_COLOR = (20, 20, 40)
# Paddle
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
PADDLE_COLOR = (200, 200, 200)
paddle_x = SCREEN_WIDTH // 2 - PADDLE_WIDTH // 2
paddle_y = SCREEN_HEIGHT - 40
# Ball
BALL_RADIUS = 8
BALL_COLOR = (255, 255, 255)
ball_x = SCREEN_WIDTH // 2
ball_y = SCREEN_HEIGHT // 2
# Bricks
BRICK_ROWS = 5
BRICK_COLS = 10
BRICK_WIDTH = 70
BRICK_HEIGHT = 20
BRICK_PADDING = 5
BRICK_OFFSET_X = 30
BRICK_OFFSET_Y = 50
BRICK_COLORS = [
(231, 76, 60), # Red
(230, 126, 34), # Orange
(241, 196, 15), # Yellow
(46, 204, 113), # Green
(52, 152, 219), # Blue
]
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Brick Breaker - Chapter 2")
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
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 = BRICK_COLORS[row]
pygame.draw.rect(screen, color, (x, y, BRICK_WIDTH, BRICK_HEIGHT))
# 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)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()
Download
Download the source code for this chapter: brick-breaker-ch02.zip
