Breakout Game


Professional project
Python code solution and explanation
turtle library

Introduction

Breakout is a classic arcade game that was released in 1976. In this game, the player controls a paddle at the bottom and uses it to deflect a ball upwards to break a wall of bricks. The objective is to clear all the bricks without the ball touching the floor. Despite its age, breakout remains a popular game and is enjoyed by many players today.

Today, we’re going to make breakout in Python, for you to impress your friends and family!

Step Breakdown

Let’s break this game down into smaller steps.

  1. Make game window
  2. Display all bricks
  3. Paddle movement
  4. Ball movement
  5. Detect collision with walls and bricks
  6. Scoreboard

With this overview, we broadly know what to do and we can check each item while making our game. 

Solution

We’ll have 5 different files, main.py, bricks.py, paddle.py, ball.py, and scoreboard.py. The last 4 files will have their own class and functions, while the main file will execute all the code.

main.py

from turtle import Screen
from paddle import Paddle
from bricks import Bricks
from ball import Ball
from scoreboard import Scoreboard

Now we can make a screen. You can choose your own customization. Make sure to turn screen.tracer() to 0, so that there’s no delay when the game runs.

screen = Screen()
screen.bgcolor("black")
screen.setup(width=800, height=600)
screen.title("Breakout")
screen.tracer(0)
breakout game screen

Set each class to an object. Right now we’re just going to code and name all the functions, and later we will make them.

bricks = Bricks()
paddle = Paddle()
ball = Ball()
scoreboard = Scoreboard()

When the arrow keys are pressed, execute its corresponding function.

screen.listen()
screen.onkey(paddle.go_right, "Right")
screen.onkeypress(paddle.go_right, "Right")
screen.onkey(paddle.go_left, "Left")
screen.onkeypress(paddle.go_left, "Left")

While not game_over, update the screen and move the ball.

game_over = False
while not game_over:
    screen.update()
    ball.move()

If the y coordinate of the ball touches the floor, the game is over and display the message.

    # Detect collision with floor (game over)
    if ball.ycor() < -280:
        game_over = True
        scoreboard.game_over()

However, if the x coordinate of the ball touches the side walls, bounce the ball on x-axis.

    # Detect collision with sides
    if ball.xcor() > 380 or ball.xcor() < -380:
        ball.bounce_x()

If the y coordinate of the ball touches the ceiling, bounce the ball on y-axis.

    # Detect collision with ceiling
    if ball.ycor() > 280:
        ball.bounce_y()

If the ball’s x and y coordinates are close to the paddle, bounce the ball on y-axis.

    # Detect collision with paddle
    if ball.distance(paddle) < 120 and ball.ycor() < -235:
        ball.bounce_y()

For each brick in all_bricks, if the x and y coordinates of the ball is close to the brick, bounce the ball on the y-axis, and remove that brick from all_bricks.

    # Detect collision with brick
    for brick in bricks.all_bricks:
        if ball.distance(brick) < 70 and ball.ycor() > brick.position()[1] - 30:
            ball.bounce_y()
            bricks.remove_brick(brick)

If all_bricks is empty, or there are no bricks left, the game is over and print a message.

    # Check if no bricks left (winner)
    if not bricks.all_bricks:
        game_over = True
        scoreboard.you_win()

bricks.py

We can predefine the bricks positions. These are the coordinates I found best.

from turtle import Turtle
BRICKS_POSITIONS = [(-330, 150), (-195, 150), (-60, 150), (75, 150), (210, 150), (345, 150),
                    (-330, 110), (-195, 110), (-60, 110), (75, 110), (210, 110), (345, 110),
                    (-330, 70), (-195, 70), (-60, 70), (75, 70), (210, 70), (345, 70),
                    (-330, 30), (-195, 30), (-60, 30), (75, 30), (210, 30), (345, 30),
                    (-330, -10), (-195, -10), (-60, -10), (75, -10), (210, -10), (345, -10),
                    (-330, -50), (-195, -50), (-60, -50), (75, -50), (210, -50), (345, -50)]

All the bricks will be saved into self.all_bricks. We can then call self.create_bricks().

class Bricks:
    def __init__(self):
        self.all_bricks = []
        self.create_bricks()

For create_bricks(), for position in BRICKS_POSITIONS, we can call self.add_brick(position)

    def create_bricks(self):
        for position in BRICKS_POSITIONS:
            self.add_brick(position)

For add_brick(position), it will make a new_brick and go to the position specified. Then it will append new_brick to self.all_bricks.

    def add_brick(self, position):
        new_brick = Turtle("square")
        new_brick.color("brown")
        new_brick.shapesize(stretch_wid=1.5, stretch_len=6.4)
        new_brick.penup()
        new_brick.goto(position)
        self.all_bricks.append(new_brick)

For remove_brick(brick), we can remove the brick from the list but that won’t get rid of it from the screen. We can send the brick outside of the screen.

    def remove_brick(self, brick):
        self.all_bricks.remove(brick)
        brick.goto(1000, 1000)

ball.py

Our Ball() class will inherit from the Turtle library. The ball will go right on top of the paddle to start off. self.x_move and self.y_move will be used later.

from turtle import Turtle


class Ball(Turtle):

    def __init__(self):
        super().__init__()
        self.color("white")
        self.shape("circle")
        self.penup()
        self.x_move = 1
        self.y_move = 1
        self.goto(0, -225)

For move(), new_x will equal the current x coordinate + self.x_move, and new_y will equal the current y coordinate + self.y_move. Then we can send the ball to this new x and y coordinate.

    def move(self):
        new_x = self.xcor() + self.x_move
        new_y = self.ycor() + self.y_move
        self.goto((new_x, new_y))

For bounce_x, we will multiply x_move by -1. Same with bounce_y. The next time move() runs, the new_x or new_y will be subtracted.

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

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

scoreboard.py

I’m going to save the font information to a variable, to make it easier to reference.

from turtle import Turtle
FONT = ("Arial", 50, "italic")

Our Scoreboard() class will also inherit from the Turtle library. Remember to hide the turtle.

class Scoreboard(Turtle):
    def __init__(self):
        super().__init__()
        self.score = 0
        self.color("white")
        self.penup()
        self.goto(0, 260)
        self.hideturtle()

For you_win(), it will go to the center of the screen and write “you win”.

    def you_win(self):
        self.goto(0, 0)
        self.write("YOU WIN!!!", align="center", font=FONT)

For game_over(), it will go to the center of the screen and write “game over”.

    def game_over(self):
        self.goto(0, 0)
        self.write("GAME OVER", align="center", font=FONT)

That sums up our game.

Gameplay
Final Code

To view my full code, please visit my GitHub repository:
https://github.com/Gursehaj-Singh/breakout-game

main.py

from turtle import Screen
from paddle import Paddle
from bricks import Bricks
from ball import Ball
from scoreboard import Scoreboard

screen = Screen()
screen.bgcolor("black")
screen.setup(width=800, height=600)
screen.title("Breakout")
screen.tracer(0)

bricks = Bricks()
paddle = Paddle()
ball = Ball()
scoreboard = Scoreboard()

screen.listen()
screen.onkey(paddle.go_right, "Right")
screen.onkeypress(paddle.go_right, "Right")
screen.onkey(paddle.go_left, "Left")
screen.onkeypress(paddle.go_left, "Left")

game_over = False
while not game_over:
    screen.update()
    ball.move()

    # Detect collision with floor (game over)
    if ball.ycor() < -280:
        game_over = True
        scoreboard.game_over()

    # Detect collision with sides
    if ball.xcor() > 380 or ball.xcor() < -380:
        ball.bounce_x()

    # Detect collision with ceiling
    if ball.ycor() > 280:
        ball.bounce_y()

    # Detect collision with paddle
    if ball.distance(paddle) < 120 and ball.ycor() < -235:
        ball.bounce_y()

    # Detect collision with brick
    for brick in bricks.all_bricks:
        if ball.distance(brick) < 70 and ball.ycor() > brick.position()[1] - 30:
            ball.bounce_y()
            bricks.remove_brick(brick)

    # Check if no bricks left (winner)
    if not bricks.all_bricks:
        game_over = True
        scoreboard.you_win()


screen.exitonclick()

bricks.py

from turtle import Turtle
BRICKS_POSITIONS = [(-330, 150), (-195, 150), (-60, 150), (75, 150), (210, 150), (345, 150),
                    (-330, 110), (-195, 110), (-60, 110), (75, 110), (210, 110), (345, 110),
                    (-330, 70), (-195, 70), (-60, 70), (75, 70), (210, 70), (345, 70),
                    (-330, 30), (-195, 30), (-60, 30), (75, 30), (210, 30), (345, 30),
                    (-330, -10), (-195, -10), (-60, -10), (75, -10), (210, -10), (345, -10),
                    (-330, -50), (-195, -50), (-60, -50), (75, -50), (210, -50), (345, -50)]


class Bricks:
    def __init__(self):
        self.all_bricks = []
        self.create_bricks()

    def create_bricks(self):
        for position in BRICKS_POSITIONS:
            self.add_brick(position)

    def add_brick(self, position):
        new_brick = Turtle("square")
        new_brick.color("brown")
        new_brick.shapesize(stretch_wid=1.5, stretch_len=6.4)
        new_brick.penup()
        new_brick.goto(position)
        self.all_bricks.append(new_brick)

    def remove_brick(self, brick):
        self.all_bricks.remove(brick)
        brick.goto(1000, 1000)

paddle.py

from turtle import Turtle


class Paddle(Turtle):

    def __init__(self):
        super().__init__()
        self.shape("square")
        self.color("white")
        self.shapesize(stretch_wid=1, stretch_len=10)
        self.penup()
        self.goto(0, -250)

    def go_right(self):
        new_x = self.xcor() + 40
        self.goto(new_x, self.ycor())

    def go_left(self):
        new_x = self.xcor() - 40
        self.goto(new_x, self.ycor())

ball.py

from turtle import Turtle


class Ball(Turtle):

    def __init__(self):
        super().__init__()
        self.color("white")
        self.shape("circle")
        self.penup()
        self.x_move = 1
        self.y_move = 1
        self.goto(0, -225)

    def move(self):
        new_x = self.xcor() + self.x_move
        new_y = self.ycor() + self.y_move
        self.goto((new_x, new_y))

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

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

scoreboard.py

from turtle import Turtle
FONT = ("Arial", 50, "italic")


class Scoreboard(Turtle):
    def __init__(self):
        super().__init__()
        self.score = 0
        self.color("white")
        self.penup()
        self.goto(0, 260)
        self.hideturtle()

    def you_win(self):
        self.goto(0, 0)
        self.write("YOU WIN!!!", align="center", font=FONT)

    def game_over(self):
        self.goto(0, 0)
        self.write("GAME OVER", align="center", font=FONT)
Further Steps

Now that we’ve built the main functionality of breakout, you may want to include other things to enhance the gameplay. This could include:

  • Power-ups
  • Obstacles, such as rocks or barriers
  • Levels with increasing difficulty
  • Multiplayer mode so 2 people can play on the same screen

Hope you enjoyed.

Related

Snake Game

,