pyodide: loading…

[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.SRCALPHA for circular surface
  • Float position separate from rect

Video 6: Ball Class Methods

  • update() with wall bounce logic
  • active flag for spacebar launch
  • Wait until player is ready

Video 7: Ball and Platform Collision

  • colliderect() detection
  • Check vy > 0 before 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 > 0 before 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]

# example 01 · player lives system

Step 2 option (10pts); track lives, reset ball on fall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🐍
Loading PythonSetting up pandas & numpy...

pygame needs a real window — copy this into a .py file and run it locally.

# example 02 · randomized ball launch

Step 2 option (5pts); random angle on spacebar

1
2
3
4
5
6
7
8
9
10
11
12
13
🐍
Loading PythonSetting up pandas & numpy...

pygame needs a real window — copy this into a .py file and run it locally.

# challenges [2]

# challenge 01/02todo
Why check 'vy > 0' before bouncing off the paddle?
pygame needs a real window. copy this into a .py file and run it locally.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
🐍
Loading PythonSetting up pandas & numpy...
# challenge 02/02todo
What does the 'offset trick' do for paddle collision?
pygame needs a real window. copy this into a .py file and run it locally.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
🐍
Loading PythonSetting up pandas & numpy...