Code for How to Build a Breakout Game with PyGame in Python Tutorial


View on Github

ball.py

import pygame as pg
from settings import ball_x_speed, ball_y_speed, ball_radius


class Ball:

    def __init__(self, x, y, screen):
        self.x = x
        self.y = y
        self.screen = screen
        self.radius = ball_radius
        self.color = pg.Color("grey")
        self.x_speed = ball_x_speed
        self.y_speed = ball_y_speed

    def move(self):
        pg.draw.circle(self.screen, self.color, [self.x, self.y], self.radius)
        self.y -= self.y_speed
        self.x -= self.x_speed

    def bounce_x(self):
        self.x_speed *= -1

    def bounce_y(self):
        self.y_speed *= -1

    def check_for_contact_on_x(self):
        if self.x - self.radius <= 0 or self.x + self.radius >= self.screen.get_width():
            self.bounce_x()

    def check_for_contact_on_y(self):
        if self.y + self.radius <= 0:
            self.bounce_y()

bricks.py

import random
import pygame as pg


class Bricks:
    def __init__(self, screen, width, height):
        self.screen = screen
        self.width = width
        self.height = height
        self.random_colors = ['blue', 'yellow', 'red', 'green', 'orange']
        self.bricks = []
        self.brick_colors = []
        self.set_values()

    def set_values(self):
        y_values = [int(y) for y in range(100, 200, 25)]
        x_values = [int(x) for x in range(10, 550, 42)]
        y_index = 0
        self.loop(x_values, y_values, y_index)

    def loop(self, x_values, y_values, y_index):

        for n in x_values:
            # Check if it is the last position in the x_values list.
            if n == x_values[-1]:

                # Check if all the positions in the y_values has been occupied
                if y_index < len(y_values) - 1:
                    y_index += 1

                    # Run the method again if there are still vacant positions.
                    self.loop(x_values, y_values, y_index)

            # Create new bricks
            else:
                x = n
                y = y_values[y_index]
                brick = pg.Rect(x, y, self.width, self.height)
                self.bricks.append(brick)
                self.brick_colors.append(random.choice(self.random_colors))

    def show_bricks(self):
        for loop in range(len(self.bricks)):
            brick = self.bricks[loop]
            color = self.brick_colors[loop]
            pg.draw.rect(self.screen, color, brick)

main.py

import pygame as pg
from paddle import Paddle
from bricks import Bricks
from ball import Ball
from scores import ScoreBoard
from settings import *


pg.init()


screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Breakout Game")

clock = pg.time.Clock()


# OBJECTS
pad = Paddle(paddle_x, paddle_y)
bricks = Bricks(screen, brick_width, brick_height)
ball = Ball(ball_x, ball_y, screen)
score = ScoreBoard(text_x, color, screen)
score.set_high_score()


sound_played = False
running = True
while running:
    screen.fill(BG_COLOR)
    score.show_scores()
    pad.appear(screen)
    bricks.show_bricks()

    # Check for quit game
    for event in pg.event.get():
        if event.type == pg.QUIT:
            # score.record_high_score()
            running = False

    # Check if there are more trials
    if score.is_game_over():
        if not sound_played:
            pg.mixer.Sound.play(game_end)
            sound_played = True
        score.game_over()

    # Check if all bricks are broken
    elif len(bricks.bricks) <= 0:
        if not sound_played:
            pg.mixer.Sound.play(win_game)
            sound_played = True
        score.success()

    else:
        ball.move()

    # Check if ball hits the x-axis above
    ball.check_for_contact_on_x()

    # Check if ball hits y-axis
    ball.check_for_contact_on_y()

    # Check if ball falls off
    if ball.y + ball.radius >= 580:
        pg.mixer.Sound.play(dropping_ball)
        ball.y = pad.y - ball.radius
        pg.time.delay(2000)
        score.trials -= 1
        ball.bounce_y()

    # Check if ball hits paddle
    if (pad.rect.y < ball.y + ball.radius < pad.rect.y + pad.height
            and
            pad.rect.x < ball.x + ball.radius < pad.rect.x + pad.width):

        pg.mixer.Sound.play(pad_hit)
        ball.bounce_y()
        ball.y = pad.y - ball.radius

    # Check if ball hits brick
    for brick in bricks.bricks:
        if brick.collidepoint(ball.x, ball.y - ball.radius) or brick.collidepoint(ball.x, ball.y + ball.radius):
            pg.mixer.Sound.play(brick_breaking)
            bricks.bricks.remove(brick)
            ball.bounce_y()
            score.score += 1

    # Check for key presses
    keys = pg.key.get_pressed()
    if keys[pg.K_RIGHT]:
        pad.move_right()

    if keys[pg.K_LEFT]:
        pad.move_left()

    # Restart game
    if keys[pg.K_0]:
        if score.is_game_over():
            score.score = 0
            score.trials = 5
            score.sound_played = False
            bricks.bricks.clear()
            bricks.set_values()

    pg.display.flip()
    clock.tick(60)

paddle.py

import pygame as pg
from settings import paddle_height, paddle_width


class Paddle:

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.width = paddle_width
        self.height = paddle_height
        self.rect = pg.Rect(self.x, self.y, self.width, self.height)
        self.color = pg.Color("white")

    def appear(self, screen):
        pg.draw.rect(screen, self.color, self.rect)

    def move_right(self):
        if self.rect.x + self.width <= 550:
            self.rect.x += 2

    def move_left(self):
        if self.rect.x >= 0:
            self.rect.x -= 2

scores.py

import pygame as pg


class ScoreBoard:
    def __init__(self, x, color, screen):
        self.screen = screen
        self.color = color
        self.x = x
        self.score = 0
        self.high_score = 0
        self.trials = 5
        self.font = pg.font.SysFont("calibri", 20)

    def show_scores(self):
        score_text = self.font.render(f"Score: {self.score}", True, self.color)
        high_score_text = self.font.render(f"High Score: {self.high_score}", True, self.color)
        trials_text = self.font.render(f"Trials: X{self.trials}", True, self.color)

        score_text_rect = score_text.get_rect(topleft=(self.x, 10))
        high_score_text_rect = high_score_text.get_rect(topleft=(self.x, 26))
        trials_text_rect = trials_text.get_rect(topleft=(self.x, 42))

        self.screen.blit(score_text, (self.x, 10))
        self.screen.blit(high_score_text, (self.x, 26))
        self.screen.blit(trials_text, (self.x, 42))

    def is_game_over(self):
        if self.trials == 0:
            return True
        return False

    def game_over(self):
        game_over_color = 'red'
        game_over_font = pg.font.SysFont("calibri", 30)
        game_over_text = game_over_font.render(f"Game Over! Click '0' to restart.", True, game_over_color)
        game_over_rect = game_over_text.get_rect(topright=(50, 300))
        self.screen.blit(game_over_text, (50, 300))
        self.record_high_score()

    def success(self):
        game_success_color = 'green'
        game_success_font = pg.font.SysFont("calibri", 30)
        game_success_text = game_success_font.render(f"You won! Click '0' to restart.", True, game_success_color)
        game_success_rect = game_success_text.get_rect(topleft=(50, 300))
        self.screen.blit(game_success_text, (50, 300))
        self.record_high_score()

    def set_high_score(self):
        try:
            with open("records.txt", mode="r") as file:
                lines = file.readlines()
        except FileNotFoundError:
            with open("records.txt", mode="w") as data:
                data.write("0")
                score = 0
        else:
            score = lines[0]

        self.high_score = int(score)

    def record_high_score(self):
        if self.score > self.high_score:
            with open("records.txt", mode="w") as file:
                file.write(f"{self.score}")

settings.py

import pygame as pg

# Initialize Sound
pg.mixer.init()

# Audio files
pad_hit = pg.mixer.Sound('audio/pad_hit.ogg')
brick_breaking = pg.mixer.Sound("audio/brick_breaking.ogg")
game_end = pg.mixer.Sound("audio/game_end_.ogg")
dropping_ball = pg.mixer.Sound("audio/dropping_ball.ogg")
win_game = pg.mixer.Sound("audio/win_game.ogg")


# Screen dimensions
WIDTH = 550
HEIGHT = 600

BG_COLOR = "purple"

# Text color
color = "white"


# Paddle settings
paddle_x = 200
paddle_y = 550
paddle_width = 100
paddle_height = 20


# Ball settings
ball_x = 250
ball_y = 540
ball_x_speed = 2
ball_y_speed = 2
ball_radius = 5


# Text settings
text_x = 300


# Bricks settings
brick_width = 40
brick_height = 20