Breakout in Python
Pygame Online · 175 lines · Left and Right, Up or Space to launch
Clear every brick with the ball, and keep it off the floor.
How it works
Sixty bricks in a list
Six rows of ten pygame.Rects. Each row has its own colour, and higher rows score more.
def new_game(): global bricks, score, lives, state bricks = [] for row in range(len(ROW_COLORS)): for col in range(10): bricks.append(pygame.Rect(6 + col * 47, BRICK_TOP + row * 18, 44, 14)) score = 0 lives = 3 state = "serve" # "serve", "playing", "over" or "won"
One axis at a time
The ball moves sideways first and checks for walls and bricks, then up or down and checks again. Splitting the move tells the game which side of a brick was hit, so the ball bounces the right way.
# Move sideways first: bounce off the side walls and bricks. ball_x += ball_dx ball.x = round(ball_x) if ball.left < 0 or ball.right > WIDTH: ball_dx = -ball_dx ball_x = max(0, min(ball_x, WIDTH - ball.width)) ball.x = round(ball_x) brick = break_brick() if brick: if ball_dx > 0: ball.right = brick.left else: ball.left = brick.right ball_x = ball.x ball_dx = -ball_dx
Aiming with the paddle
Where the ball lands on the paddle decides its new angle: up to 60 degrees to the left or right of straight up. launch turns that angle into a speed across and a speed up.
def launch(offset): """Send the ball up. offset -1 is far left, 0 straight up, 1 far right.""" global ball_dx, ball_dy angle = math.radians(offset * 60) ball_dx = ball_speed() * math.sin(angle) ball_dy = -ball_speed() * math.cos(angle)
Make it yours
Change the code above and press Run again. Some ideas:
- Add a row of strong bricks that need two hits.
- Make the paddle wider for the first ten seconds of each life.
- Speed the ball up faster: change
0.05inball_speed. - Add a second level with a different brick pattern once the first is cleared.
The whole program
Plain pygame: it runs the same on a computer with Python and pygame installed.
import math import random import pygame pygame.init() WIDTH, HEIGHT = 480, 360 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Breakout") clock = pygame.time.Clock() font = pygame.font.Font(None, 28) big_font = pygame.font.Font(None, 60) BG = (16, 18, 22) YELLOW = (255, 212, 59) BLUE = (48, 105, 152) TEXT = (230, 232, 235) # One colour per row of bricks, top to bottom. ROW_COLORS = [(235, 95, 85), (240, 150, 60), (255, 212, 59), (110, 200, 120), (90, 162, 224), (48, 105, 152)] BRICK_TOP = 50 PADDLE_SPEED = 7 START_KEYS = (pygame.K_UP, pygame.K_SPACE, pygame.K_RETURN) paddle = pygame.Rect(WIDTH // 2 - 35, HEIGHT - 30, 70, 10) ball = pygame.Rect(0, 0, 10, 10) def new_game(): global bricks, score, lives, state bricks = [] for row in range(len(ROW_COLORS)): for col in range(10): bricks.append(pygame.Rect(6 + col * 47, BRICK_TOP + row * 18, 44, 14)) score = 0 lives = 3 state = "serve" # "serve", "playing", "over" or "won" def ball_speed(): """The ball gets faster as bricks disappear.""" return min(4 + (60 - len(bricks)) * 0.05, 7) def launch(offset): """Send the ball up. offset -1 is far left, 0 straight up, 1 far right.""" global ball_dx, ball_dy angle = math.radians(offset * 60) ball_dx = ball_speed() * math.sin(angle) ball_dy = -ball_speed() * math.cos(angle) def break_brick(): """Remove the brick the ball touches, if any, and score it.""" global score i = ball.collidelist(bricks) if i == -1: return None brick = bricks.pop(i) row = (brick.y - BRICK_TOP) // 18 score += len(ROW_COLORS) - row # higher rows are worth more return brick def draw_text(text, text_font, color, center): image = text_font.render(text, True, color) screen.blit(image, image.get_rect(center=center)) new_game() ball_x = ball_y = ball_dx = ball_dy = 0 wait = 0 # frames until a new game may start running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN and event.key in START_KEYS: if state == "serve": launch(random.uniform(-0.5, 0.5)) state = "playing" elif state in ("over", "won") and wait == 0: new_game() keys = pygame.key.get_pressed() if state in ("serve", "playing"): if keys[pygame.K_LEFT]: paddle.x -= PADDLE_SPEED if keys[pygame.K_RIGHT]: paddle.x += PADDLE_SPEED paddle.clamp_ip(screen.get_rect()) if wait > 0: wait -= 1 if state == "serve": # The ball rides on the paddle until it is launched. ball.midbottom = paddle.midtop ball_x, ball_y = ball.x, ball.y elif state == "playing": # Move sideways first: bounce off the side walls and bricks. ball_x += ball_dx ball.x = round(ball_x) if ball.left < 0 or ball.right > WIDTH: ball_dx = -ball_dx ball_x = max(0, min(ball_x, WIDTH - ball.width)) ball.x = round(ball_x) brick = break_brick() if brick: if ball_dx > 0: ball.right = brick.left else: ball.left = brick.right ball_x = ball.x ball_dx = -ball_dx # Then up or down: the ceiling, bricks and the paddle. ball_y += ball_dy ball.y = round(ball_y) if ball.top < 0: ball_dy = -ball_dy ball_y = ball.y = 0 brick = break_brick() if brick: if ball_dy > 0: ball.bottom = brick.top else: ball.top = brick.bottom ball_y = ball.y ball_dy = -ball_dy if ball_dy > 0 and ball.colliderect(paddle): # Where it lands on the paddle decides the angle it leaves at. offset = (ball.centerx - paddle.centerx) / (paddle.width / 2) launch(max(-1, min(1, offset))) ball.bottom = paddle.top ball_y = ball.y if not bricks: state = "won" wait = 30 elif ball.top > HEIGHT: lives -= 1 if lives > 0: state = "serve" else: state = "over" wait = 30 screen.fill(BG) for brick in bricks: row = (brick.y - BRICK_TOP) // 18 pygame.draw.rect(screen, ROW_COLORS[row], brick, border_radius=3) pygame.draw.rect(screen, YELLOW, paddle, border_radius=5) if state in ("serve", "playing"): pygame.draw.circle(screen, TEXT, ball.center, 5) screen.blit(font.render(f"Score {score}", True, TEXT), (12, 14)) for i in range(lives): pygame.draw.circle(screen, YELLOW, (WIDTH - 20 - i * 20, 22), 6) if state == "serve": draw_text("Left and Right move the paddle", font, TEXT, (WIDTH // 2, 230)) draw_text("Press Up or Space to launch", font, YELLOW, (WIDTH // 2, 260)) elif state in ("over", "won"): panel = pygame.Rect(0, 0, 360, 130) panel.center = (WIDTH // 2, 220) pygame.draw.rect(screen, BG, panel, border_radius=12) pygame.draw.rect(screen, BLUE, panel, 2, border_radius=12) title = "You win!" if state == "won" else "Game over" draw_text(title, big_font, YELLOW, (panel.centerx, panel.y + 45)) draw_text("Press Up or Space to play again", font, TEXT, (panel.centerx, panel.y + 98)) pygame.display.flip() clock.tick(60) pygame.quit()