Unlock the secrets of your code with ourAI-powered Code Explainer. Take a look!
Programming involves your ability to manipulate code effectively. This is the quality that allows you to build worthy and thought provoking applications.
Building game projects is one of the best ways to learn code manipulation. In Python, a popular library used for building interactive games isPygame.
In this tutorial, you will learn how to build aBreakout game in Python. It is a classic game featuring a movable paddle, a bouncing ball, and multiple overhead bricks.
The objective of this game is to use the ball to destroy all the bricks while using the paddle to control the ball. You can check out a demo of the game below:
By the end of this tutorial, you will learn how to build a fully interactive game using Pygame and manipulate the code to meet your specific needs.
Table of Contents:
To follow along with this tutorial, you need the following:
1.Pygame: Run the command below to install pygame.
pip install pygame2. Freesound account: We will need to add sounds when certain events occur in our game.Freesound has a library of free soundtracks from which you can get game sounds. You need to create an account to download the sounds.
The first step is to create asettings.py file in the project folder. This file will contain all the settings and variables for the game. Below is the content of thesettings.py file:
# settings.pyimport pygame as pg# Screen settingsWIDTH = 550HEIGHT = 600BG_COLOR = "purple"# Text colorcolor = "white"# Paddle settingspaddle_x = 250paddle_y = 550paddle_width = 100paddle_height = 20# Ball settingsball_x = 250ball_y = 540ball_x_speed = 2ball_y_speed = 2ball_radius = 5ball_color = "grey"# Text settingstext_x = 300# Bricks settingsbrick_width = 40brick_height = 20As stated earlier, a breakout game contains apaddle,ball, set of bricks, and a scoreboard to keep track of scores. Therefore, the settings above will be used to set up the size, dimension, position (x, y), and other features of each object in the game.
We must set up the game screen by specifying a suitable dimension, color, etc. Create a new file namedmain.py. This file will serve as the main script for the game. Add the code below to this file:
# main.pyimport pygame as pgfrom settings import *# Initialize pygamepg.init()screen = pg.display.set_mode((WIDTH, HEIGHT))pg.display.set_caption("Breakout Game")clock = pg.time.Clock() running = Truewhile running: screen.fill(BG_COLOR) # Check for quit game for event in pg.event.get(): if event.type == pg.QUIT: running = False pg.display.flip() # To adjust the game frame rate clock.tick(60)The code above is like a boilerplate for setting up a game window in pygame. We first initialized pygame and set the window dimension already using the variables in thesettings.py file.
TheClock() class is used to manage the frame rate per second. This ensures the game does not run too fast and is consistent on all systems. To keep the window open, we need to create awhile loop where every event in the game will be added.
Finally, we set up a loop that quits the game when a user closes the game window. Thepg.display.flip() method ensures every object and event that occurs in the game is updated regularly.
The screen is currently blank, so we need to add objects. The first object we will add is the paddle. To add the paddle, you need to create a new python file namedpaddle.py. It is in this file that we will create ourPaddle class.
Since our project requires us to add objects such as paddles, balls, bricks, etc. It is recommended that we split each one of them into separate classes.
Add the following code to thepaddle.py file:
# paddle.pyimport pygame as pgfrom settings import paddle_height, paddle_widthclass 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 -= 2In the code above, we created aPaddle class and set its properties, such as thex andy positions on the screen, height, width, and color. The paddle is created from apygame rectangle object, which is represented by theself.rect attribute.
Theappear() method displays the paddle on the screen.
The other two methods -move_right() andmove_left() add motion to the Paddle by moving it to the right and left by 2 steps, respectively. These methods also ensure that the paddle only moves within the screen dimensions. Therefore, if you try to move the paddle beyond the screen, it will not move.
We need to add the paddle object to the main script. Open themain.py file and do the following:
1. Import thepaddle class and create a paddle object
from paddle import Paddle...# OBJECTSpad = Paddle(paddle_x, paddle_y)...2. Add theappear() method to thewhile loop like below:
while running: ... pad.appear(screen) ...3. Check for key presses to move the ball to either the right or left:
while running: ... # 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() ...Yourmain.py file should now look like the one below:
# main.pyimport pygame as pgfrom paddle import Paddlefrom settings import *pg.init()screen = pg.display.set_mode((WIDTH, HEIGHT))pg.display.set_caption("Breakout Game")clock = pg.time.Clock()# OBJECTSpad = Paddle(paddle_x, paddle_y)running = Truewhile running: screen.fill(BG_COLOR) pad.appear(screen) # Check for quit game for event in pg.event.get(): if event.type == pg.QUIT: running = False # 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() pg.display.flip() clock.tick(60)Run the program. You should see a paddle appear on the screen like the one below:

Use the right and left arrow keys to control the paddle.
Just as we did for the paddle, create a new file namedball.py and create aBall class. Add the code below to this file:
# ball.pyimport pygame as pgfrom settings import ball_x_speed, ball_y_speed, ball_radius, ball_colorclass Ball: def __init__(self, x, y, screen): self.x = x self.y = y self.screen = screen self.radius = ball_radius self.color = pg.Color(ball_color) 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_speedIn the code above, we created a class for the ball and set its properties. Themove() function draws the ball at the specified coordinate on the screen and adds motion to it. It achieves this by reducing thex andy values of the ball.
Follow the steps below to add the ball to the screen:
1. Open themain.py file
2. Import theBall class and create a ball object:
...from ball import Ball...# OBJECTSball = Ball(ball_x, ball_y, screen)3. Add the.move() method to thewhile loop:
running = Truewhile running: ... ball.move() ...This will ensure the ball’s position is regularly updated on the screen.
The ball just keeps rolling off the screen. Originally, it was supposed to bounce anytime it hit the screen or the paddle edges (except for the bottom part of the screen). We need to add new methods to theBall() class to implement this. Below are the methods:
...def bounce_x(self): self.x_speed *= -1def bounce_y(self): self.y_speed *= -1def 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()Thebounce_x() andbounce_y() methods implement the ball-bouncing logic for thex andy axis respectively.
Thecheck_for_contact_on_y() andcheck_for_contact_on_x() methods compare the ball’s distance with the screen edges using its radius and currentx andy positions.
These methods create a reverse of the ball's current position by multiplying it by-1. For example, if the ball is moving upward, then it hits the screen on the right, we need to make it bounce back to the left by reducing itsx andy value. The only way to do this is by multiplying it by-1, so it becomes negative and starts to move towards the left side.
Similarly, if the ball is moving upward and it hits the screen on the left, we need to make it bounce to the right by increasing itsx andy value.
Since,x is negative already, the only way to make it positive is by multiplying it by-1, so it starts to move towards the right side.
Let’s reflect these changes in thewhile loop as seen below:
running = Truewhile running: ... ball.move() ... # Check if ball hits the x-axis above ball.check_for_contact_on_x() # Check if ball hits y-axis on the side ball.check_for_contact_on_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): ball.bounce_y() ball.y = pad.y - ball.radius ...The ball will bounce when it hits the screen edges or the paddle.
The bricks in a breakout game are always above the paddle. They usually appear in different colors. We will need to do some mathematics to add the bricks correctly. Don't fret; it's not rocket science. I'll show you how to go about it.
Create a new Python file namedbricks.py and add a class namedBricks:
# bricks.pyimport randomimport pygame as pgclass 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)In theBricks class, we added the brick properties and other attributes, which include the following:
self.random_colors: This is a list of colors that the bricks can pick fromself.bricks: This is a list that will contain all the bricks generated for the game self.brick_colors: This list will contain the already selected colors for each brick created in the game.Theset_values() method creates a list of values for thex and y-axis. They_index will be used to access each value for the y-axis in the list. But for now, it is0.
Theloop() method takes three positional arguments. They are the variables we added to theset_values() method earlier.
Now, what does the code in theloop() method do?
We already have a list ofx andy positions for our bricks. All that is needed is to place them on each point. One way to do this is by selecting a position on the y-axis, and arrange the bricks along this position, with each brick having an x-value from the list of generated positions forx.
They_index increases by1 when a particular y-axis is filled up. This way, it moves to the next y-axis on the next loop. This continues until all the y-axis are occupied. Each brick is then added to theself.bricks list.
Finally, theshow_bricks() method displays the bricks on the screen by looping through theself.bricks list and give each brick a color from theself.brick_colors list.
Now, let’s add this to the game loop. Follow the steps below to do this:
1. Import theBricks class and create a brick object
...from bricks import Bricks...# OBJECTSbricks = Bricks(screen, brick_width, brick_height)...2. Add theshow_bricks() method to thewhile loop like below:
running = Truewhile running: ... bricks.show_bricks() ...Run the program, and you should see colorful bricks arranged on the screen like in the image below:

According to the game rules, the bricks break and disappear once the ball hits them. We can implement this by checking if the ball has collided with the brick. Add a new if block to thewhile loop to add this feature:
...running = Truewhile running: ... # 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): bricks.bricks.remove(brick) ball.bounce_y() ...In the code above, a brick is removed from the list when it gets hit, and the ball bounces back.
The next step is to record your scores for each successful hit and then reduce your trials/lives when the paddle misses the ball.
Create a new file namedscores.py. In this file, create aScoreboard class that will handle the scores. Below is theScoreBoard class:
# scores.pyimport pygame as pgclass 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 = 2 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}")In this code, we set the scoreboard properties like font, height, etc.. Theshow_scores() method displays the scores, high scores, and trials on the screen.
As in previous examples, import theScores class and create a new score object. Add the code below to thewhile loop:
...running = Truewhile running: ... # 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): bricks.bricks.remove(brick) ball.bounce_y() # Increase scores by 1 score.score += 1 # Check if ball falls off if ball.y + ball.radius >= 580: ball.y = pad.y - ball.radius pg.time.delay(2000) score.trials -= 1 ball.bounce_y()...This way, if the ball hits a brick, your score increases by1. Also, your score will be reduced if the ball drops beyond the paddle. Below is an image showing the scoreboard:

The game ends when no trials are left or all the bricks have been destroyed. We must create a condition that ends the game when the number of trials or number of bricks equals0 .
In theScoreBoard class, there are three methods namelygame_over(), is_game_over() andsuccess().
Theis_game_over() andgame_over() methods will be called when a user has no trials left, that is, he/she has lost, while thesuccess() method is called when a user breaks all the bricks. Each method displays a body of text describing each event.
The last two methodsset_high_score() and record_high_score() add continuity to this game. For now, you can only play for the moment and your scores are not recorded. These methods change this by keeping a record of your highest score in a.txt document. This is why they are added to thesuccess() and game_over() methods, so the high score will be updated when the game ends
Therefore, when you restart the game, your highest score will appear on the scoreboard.Let’s update the main script to contain the new updates. Add the code below to themain.py file:
...# OBJECTS...score = ScoreBoard(text_x, color, screen)score.set_high_scorerunning = Truewhile running: ... # Check if there are more trials if score.is_game_over(): score.game_over() # Check if all bricks are broken elif len(bricks.bricks) == 0: score.success() else: ball.move() ...In the code above, we update thewhile loop so that if the remaining trial equals 0 or the user breaks all the bricks, the ball should stop moving. This is why theball.move() method is moved to an if block. Therefore, the ball stops moving when any of the conditions is met.
Below is an image showing when a user lost:

Below is an image showing when a user wins:

The next step is toadd sounds to the game. We will be adding sounds for certain events in the game
As stated at the beginning of this tutorial,Freesound.org has a vast library of background sounds to use. I have selected the audio files that will be used for this tutorial. However, you are at liberty to choose anyone of your choice. Below is a list of the audio files that will be used in the game and where to use them:
An audio file format that works best for pygame is.ogg. It packages audio files into small sizes, which ensures delivery speed. You can use afree online audio converter to convert the audio files to.ogg.
After converting these files, then you can use it for the game via the steps below:
audio. settings.py file and initialize the sounds inPygame# settings.py...# Initialize Soundpg.mixer.init()# Audio filespad_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")...The code above initializes the audio sounds for the project. Open themain.py file and add sounds to the specific events as seen below:
# main.py...sound_played = Falserunning = Truewhile running: ... # Check if there are more trials if score.is_game_over(): if not sound_played: # Sound added 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: # Sound added pg.mixer.Sound.play(win_game) sound_played = True score.success() else: ball.move() ... # Check if ball falls off if ball.y + ball.radius >= 580: # Sound added 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): # Sound added 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): # Sound added pg.mixer.Sound.play(brick_breaking) bricks.bricks.remove(brick) ball.bounce_y() score.score += 1 ...The final step in this game is allowing users to restart it when they lose or win. We need to add a key listener event to restart the game when the user clicks "0". Here is the code below:
...sound_played = Falserunning = Truewhile running: ... # Check for key presses ... # 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() ...This code resets all the game variables to the default settings.
This project is an awesome way to start building games in Python. You have learned how to build a fully-fledged desktop game with the Pygame library. We were also able to add sounds to make the game more entertaining.
You can now confidently take up similar projects to build by yourself. Let me know what you think about this game, and other questions you may have for me in the comments section.
You can viewthe full code here.
Related tutorials:
Until next time, Pythonista 🐍
Take the stress out of learning Python. Meet ourPython Code Assistant – your new coding buddy. Give it a whirl!
View Full Code Assist My CodingGot a coding query or need some guidance before you comment? Check out thisPython Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!
