[challenge]Game Development with Pygame
Project: Brick Breakaway
# theory
what you're building
Brick Breakaway is a classic arcade game: a ball bounces around breaking bricks, you control a paddle at the bottom to keep the ball from falling off-screen. Clear all the bricks to win.
🎬 gerber's video breakdown (9 videos)
Follow along with Gerber's videos in order. Each video adds one piece:
Video 1: Project Setup
- Create folder structure: main.py, settings.py
- Import pygame, basic window setup
- Empty game loop with clock
Video 2: User Platform Class and Attributes
- Create Paddle class extending pygame.sprite.Sprite
- self.image, self.rect, self.speed
- Position at bottom of screen
Video 3: Basic Platform Movement
keys = pygame.key.get_pressed()- Move paddle left/right with
rect.x += speed - Clamp to screen boundaries
Video 4: Delta Time and Smooth Movements
dt = clock.tick(60) / 1000.0- Multiply velocity by dt
- Consistent speed on any machine
Video 5: Ball Class and Attributes
- Ball sprite with vx/vy as floats
pygame.SRCALPHAfor circular surface- Float position separate from rect
Video 6: Ball Class Methods
- update() with wall bounce logic
activeflag for spacebar launch- Wait until player is ready
Video 7: Ball and Platform Collision
colliderect()detection- Check
vy > 0before bouncing! - Offset trick for angled bounces based on hit position
Video 8: Brick Class
- Grid layout with nested loops
- Color by row (harder = different color)
- Rect-based collision bounds
Video 9: Brick Collisions
spritecollide()or manual rect check- Remove bricks on hit
- Determine bounce direction
project structure
BrickBreakaway/
├── main.py # game loop + main()
├── settings.py # constants (WIDTH, HEIGHT, colors, speeds)
├── paddle.py # Paddle sprite
├── ball.py # Ball with physics
└── brick.py # Brick sprite
video 1: settings & constants
# settings.py
WIDTH, HEIGHT = 800, 600
FPS = 60
PADDLE_SPEED = 400 # pixels per second (use dt!)
PADDLE_WIDTH = 120
PADDLE_HEIGHT = 15
BALL_SPEED = 350 # pixels per second
BALL_RADIUS = 10
BRICK_ROWS = 5
BRICK_COLS = 10
BRICK_WIDTH = 70
BRICK_HEIGHT = 25
BRICK_PADDING = 5
BRICK_TOP_OFFSET = 80
COLORS = {
1: (255, 80, 80), # red - 1 hit
2: (255, 165, 0), # orange - 2 hits
3: (255, 220, 0), # yellow - 3 hits
}
videos 2-3: paddle class
# paddle.py
import pygame
from settings import *
class Paddle(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((PADDLE_WIDTH, PADDLE_HEIGHT))
self.image.fill((200, 200, 255))
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH // 2
self.rect.bottom = HEIGHT - 20
self.float_x = float(self.rect.x)
def update(self, dt):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.rect.left > 0:
self.float_x -= PADDLE_SPEED * dt
if keys[pygame.K_RIGHT] and self.rect.right < WIDTH:
self.float_x += PADDLE_SPEED * dt
self.rect.x = int(self.float_x)
self.rect.clamp_ip(pygame.Rect(0, 0, WIDTH, HEIGHT))
self.float_x = float(self.rect.x)
videos 5-6: ball class
# ball.py
import pygame
from settings import *
class Ball(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((BALL_RADIUS*2, BALL_RADIUS*2), pygame.SRCALPHA)
pygame.draw.circle(self.image, (255, 255, 255), (BALL_RADIUS, BALL_RADIUS), BALL_RADIUS)
self.rect = self.image.get_rect()
self.reset()
def reset(self):
self.rect.center = (WIDTH // 2, HEIGHT // 2)
self.float_x = float(self.rect.centerx)
self.float_y = float(self.rect.centery)
self.vx = BALL_SPEED * 0.7
self.vy = -BALL_SPEED
self.active = False
def update(self, dt):
if not self.active:
return
self.float_x += self.vx * dt
self.float_y += self.vy * dt
# Wall bounces
if self.float_x - BALL_RADIUS <= 0:
self.float_x = BALL_RADIUS
self.vx = abs(self.vx)
elif self.float_x + BALL_RADIUS >= WIDTH:
self.float_x = WIDTH - BALL_RADIUS
self.vx = -abs(self.vx)
if self.float_y - BALL_RADIUS <= 0:
self.float_y = BALL_RADIUS
self.vy = abs(self.vy)
self.rect.center = (int(self.float_x), int(self.float_y))
video 7: ball + paddle collision
# In main game loop:
if ball.rect.colliderect(paddle.rect) and ball.vy > 0:
ball.vy = -abs(ball.vy) # Bounce up
# Offset trick: angle based on where ball hit paddle
offset = (ball.rect.centerx - paddle.rect.centerx) / (PADDLE_WIDTH / 2)
ball.vx = BALL_SPEED * offset * 0.8
Gerber tip: The offset trick makes the game more interesting. Hit the ball with the edge of the paddle and it bounces at an angle.
videos 8-9: brick class & collisions
# brick.py
import pygame
from settings import *
class Brick(pygame.sprite.Sprite):
def __init__(self, x, y, hits=1):
super().__init__()
self.hits = hits
self.image = pygame.Surface((BRICK_WIDTH, BRICK_HEIGHT))
self.image.fill(COLORS.get(hits, (150, 150, 150)))
self.rect = self.image.get_rect(topleft=(x, y))
def hit(self):
self.hits -= 1
if self.hits <= 0:
self.kill()
else:
self.image.fill(COLORS.get(self.hits, (150, 150, 150)))
tips
- Gerber tip: Keep all constants in settings.py; way easier to tune
- Gerber tip: Check
vy > 0before paddle bounce; prevents stuck ball - Tip: Use float positions for smooth movement, sync to rect each frame
- Tip: Process only one brick collision per frame to prevent double-bounces
common mistakes
- Forgetting vy > 0 check: ball gets stuck in paddle
- Integer positions causing jitter: use floats, convert to int for rect
- Not clamping paddle to screen: paddle escapes the window
- Multiple brick collisions per frame: break after first hit
step 2: improvement ideas (20+ points required)
- Informative Text (5pts): spacebar prompt + game over message
- Randomized Ball Start (5pts): random angle on launch
- Player Life (10pts): 3 lives, reset ball on fall
- Additional Levels (10pts): new brick layout after clearing
- Power Up Bricks (15pts): special bricks drop power-ups
# examples [2]
Step 2 option (10pts); track lives, reset ball on fall
pygame needs a real window — copy this into a .py file and run it locally.
Step 2 option (5pts); random angle on spacebar
pygame needs a real window — copy this into a .py file and run it locally.
# challenges [2]