Brick Breaker Game using Tkinter (Python Project)
It's time to code a game in Python. We will start with a simple Brick breaker game in which there is a ball that bounces of a platform to break a brick wall and the player has to keep the ball going by making sure the paddle is always there to bounce off the ball back.
The game will have three layers of bricks, and each layer of brick will have a different hit capacity, which means some bricks will break in a single hit, some will require a double hit and some will require three hits.
To simplify the logic and user interface of the game, the following are the type of bricks:
Green brick: Requires three hits to break
Pink bricks: Requires two hits to break
Purple bricks: Requires three hits to break
Prerequisites:
To understand the code to build the brick breaker game in Python, you should know the following concepts:
Apart from that the overall understanding of Python is required, which you can also get from our FREE tutorial series to Learn Python.
Code for Brick Breaker Game
We will be creating 5 classes in the code, the name of the classes are:
-
GameObject
-
Ball
-
Paddle
-
Brick
-
Game
All these classes will have methods to perform various handling and operations on the respective game element.
Let's see the code for each class.
GameObject class
This class is the parent class of all the other classes, which means all the other classes will inherit this class.
The code for this class is as follows:
class GameObject(object):
def __init__(self, canvas, item):
self.canvas = canvas
self.item = item
def get_position(self):
return self.canvas.coords(self.item)
def move(self, x, y):
self.canvas.move(self.item, x, y)
def delete(self):
self.canvas.delete(self.item)
This class has functions to handle the basic lifecycle of different objects that form the game.
Ball Class
Here is the code for the Ball class:
class Ball(GameObject):
def __init__(self, canvas, x, y):
self.radius = 10
self.direction = [1, -1]
# increase the below value to increase the speed of ball
self.speed = 5
item = canvas.create_oval(x-self.radius, y-self.radius,
x+self.radius, y+self.radius,
fill='white')
super(Ball, self).__init__(canvas, item)
def update(self):
coords = self.get_position()
width = self.canvas.winfo_width()
if coords[0] <= 0 or coords[2] >= width:
self.direction[0] *= -1
if coords[1] <= 0:
self.direction[1] *= -1
x = self.direction[0] * self.speed
y = self.direction[1] * self.speed
self.move(x, y)
def collide(self, game_objects):
coords = self.get_position()
x = (coords[0] + coords[2]) * 0.5
if len(game_objects) > 1:
self.direction[1] *= -1
elif len(game_objects) == 1:
game_object = game_objects[0]
coords = game_object.get_position()
if x > coords[2]:
self.direction[0] = 1
elif x < coords[0]:
self.direction[0] = -1
else:
self.direction[1] *= -1
for game_object in game_objects:
if isinstance(game_object, Brick):
game_object.hit()
In the Ball class the collide()
method is used to handle the collision and is used in the check_collisions() function of the Game class. Whereas the update()
function is to reset the Ball object after a life is lost and the game starts again.
Paddle Class
Here is the code for the Paddle class:
class Paddle(GameObject):
def __init__(self, canvas, x, y):
# set the shape and position of paddle
self.width = 80
self.height = 10
self.ball = None
item = canvas.create_rectangle(x - self.width / 2,
y - self.height / 2,
x + self.width / 2,
y + self.height / 2,
fill='#FFB643')
super(Paddle, self).__init__(canvas, item)
def set_ball(self, ball):
self.ball = ball
def move(self, offset):
coords = self.get_position()
width = self.canvas.winfo_width()
if coords[0] + offset >= 0 and coords[2] + offset <= width:
super(Paddle, self).move(offset, 0)
if self.ball is not None:
self.ball.move(offset, 0)
Apart from the constructor, the move()
method in this class is important as that enables the movement for the paddle.
Brick Class
Here is the code for the Brick class:
class Brick(GameObject):
COLORS = {1: '#4535AA', 2: '#ED639E', 3: '#8FE1A2'}
def __init__(self, canvas, x, y, hits):
self.width = 75
self.height = 20
self.hits = hits
color = Brick.COLORS[hits]
item = canvas.create_rectangle(x - self.width / 2,
y - self.height / 2,
x + self.width / 2,
y + self.height / 2,
fill=color, tags='brick')
super(Brick, self).__init__(canvas, item)
def hit(self):
self.hits -= 1
if self.hits == 0:
self.delete()
else:
self.canvas.itemconfig(self.item,
fill=Brick.COLORS[self.hits])
We start the class definition by setting the colors for the different brick types. Then in the constructor, the size and position of the bricks are defined. And then there is the hit()
function that handles whether the brick will disappear or it will take the hit and change color to take the next hit.
Game Class
This is the main class that controls everything. Here is the code:
class Game(tk.Frame):
def __init__(self, master):
super(Game, self).__init__(master)
self.lives = 3
self.width = 610
self.height = 400
self.canvas = tk.Canvas(self, bg='#D6D1F5',
width=self.width,
height=self.height,)
self.canvas.pack()
self.pack()
self.items = {}
self.ball = None
self.paddle = Paddle(self.canvas, self.width/2, 326)
self.items[self.paddle.item] = self.paddle
# adding brick with different hit capacities - 3,2 and 1
for x in range(5, self.width - 5, 75):
self.add_brick(x + 37.5, 50, 3)
self.add_brick(x + 37.5, 70, 2)
self.add_brick(x + 37.5, 90, 1)
self.hud = None
self.setup_game()
self.canvas.focus_set()
self.canvas.bind('<Left>',
lambda _: self.paddle.move(-10))
self.canvas.bind('<Right>',
lambda _: self.paddle.move(10))
def setup_game(self):
self.add_ball()
self.update_lives_text()
self.text = self.draw_text(300, 200,
'Press Space to start')
self.canvas.bind('<space>', lambda _: self.start_game())
def add_ball(self):
if self.ball is not None:
self.ball.delete()
paddle_coords = self.paddle.get_position()
x = (paddle_coords[0] + paddle_coords[2]) * 0.5
self.ball = Ball(self.canvas, x, 310)
self.paddle.set_ball(self.ball)
def add_brick(self, x, y, hits):
brick = Brick(self.canvas, x, y, hits)
self.items[brick.item] = brick
def draw_text(self, x, y, text, size='40'):
font = ('Forte', size)
return self.canvas.create_text(x, y, text=text,
font=font)
def update_lives_text(self):
text = 'Lives: %s' % self.lives
if self.hud is None:
self.hud = self.draw_text(50, 20, text, 15)
else:
self.canvas.itemconfig(self.hud, text=text)
def start_game(self):
self.canvas.unbind('<space>')
self.canvas.delete(self.text)
self.paddle.ball = None
self.game_loop()
def game_loop(self):
self.check_collisions()
num_bricks = len(self.canvas.find_withtag('brick'))
if num_bricks == 0:
self.ball.speed = None
self.draw_text(300, 200, 'You win! You the Breaker of Bricks.')
elif self.ball.get_position()[3] >= self.height:
self.ball.speed = None
self.lives -= 1
if self.lives < 0:
self.draw_text(300, 200, 'You Lose! Game Over!')
else:
self.after(1000, self.setup_game)
else:
self.ball.update()
self.after(50, self.game_loop)
def check_collisions(self):
ball_coords = self.ball.get_position()
items = self.canvas.find_overlapping(*ball_coords)
objects = [self.items[x] for x in items if x in self.items]
self.ball.collide(objects)
In this class, we have the following:
-
We define the number of lives, the background colors of the game, and other styling of the game window.
-
The game setup is done by this class.
-
Adding the bricks, adding the paddle, ball, and text is done by this class.
-
Handling of lives is taken care of by this class.
-
Starting the game and running the game loop, is also done in this class.
Complete Code for the Brick Breaker Game:
Here is the complete code for the game.
import tkinter as tk
class GameObject(object):
def __init__(self, canvas, item):
self.canvas = canvas
self.item = item
def get_position(self):
return self.canvas.coords(self.item)
def move(self, x, y):
self.canvas.move(self.item, x, y)
def delete(self):
self.canvas.delete(self.item)
class Ball(GameObject):
def __init__(self, canvas, x, y):
self.radius = 10
self.direction = [1, -1]
# increase the below value to increase the speed of ball
self.speed = 5
item = canvas.create_oval(x-self.radius, y-self.radius,
x+self.radius, y+self.radius,
fill='white')
super(Ball, self).__init__(canvas, item)
def update(self):
coords = self.get_position()
width = self.canvas.winfo_width()
if coords[0] <= 0 or coords[2] >= width:
self.direction[0] *= -1
if coords[1] <= 0:
self.direction[1] *= -1
x = self.direction[0] * self.speed
y = self.direction[1] * self.speed
self.move(x, y)
def collide(self, game_objects):
coords = self.get_position()
x = (coords[0] + coords[2]) * 0.5
if len(game_objects) > 1:
self.direction[1] *= -1
elif len(game_objects) == 1:
game_object = game_objects[0]
coords = game_object.get_position()
if x > coords[2]:
self.direction[0] = 1
elif x < coords[0]:
self.direction[0] = -1
else:
self.direction[1] *= -1
for game_object in game_objects:
if isinstance(game_object, Brick):
game_object.hit()
class Paddle(GameObject):
def __init__(self, canvas, x, y):
self.width = 80
self.height = 10
self.ball = None
item = canvas.create_rectangle(x - self.width / 2,
y - self.height / 2,
x + self.width / 2,
y + self.height / 2,
fill='#FFB643')
super(Paddle, self).__init__(canvas, item)
def set_ball(self, ball):
self.ball = ball
def move(self, offset):
coords = self.get_position()
width = self.canvas.winfo_width()
if coords[0] + offset >= 0 and coords[2] + offset <= width:
super(Paddle, self).move(offset, 0)
if self.ball is not None:
self.ball.move(offset, 0)
class Brick(GameObject):
COLORS = {1: '#4535AA', 2: '#ED639E', 3: '#8FE1A2'}
def __init__(self, canvas, x, y, hits):
self.width = 75
self.height = 20
self.hits = hits
color = Brick.COLORS[hits]
item = canvas.create_rectangle(x - self.width / 2,
y - self.height / 2,
x + self.width / 2,
y + self.height / 2,
fill=color, tags='brick')
super(Brick, self).__init__(canvas, item)
def hit(self):
self.hits -= 1
if self.hits == 0:
self.delete()
else:
self.canvas.itemconfig(self.item,
fill=Brick.COLORS[self.hits])
class Game(tk.Frame):
def __init__(self, master):
super(Game, self).__init__(master)
self.lives = 3
self.width = 610
self.height = 400
self.canvas = tk.Canvas(self, bg='#D6D1F5',
width=self.width,
height=self.height,)
self.canvas.pack()
self.pack()
self.items = {}
self.ball = None
self.paddle = Paddle(self.canvas, self.width/2, 326)
self.items[self.paddle.item] = self.paddle
# adding brick with different hit capacities - 3,2 and 1
for x in range(5, self.width - 5, 75):
self.add_brick(x + 37.5, 50, 3)
self.add_brick(x + 37.5, 70, 2)
self.add_brick(x + 37.5, 90, 1)
self.hud = None
self.setup_game()
self.canvas.focus_set()
self.canvas.bind('<Left>',
lambda _: self.paddle.move(-10))
self.canvas.bind('<Right>',
lambda _: self.paddle.move(10))
def setup_game(self):
self.add_ball()
self.update_lives_text()
self.text = self.draw_text(300, 200,
'Press Space to start')
self.canvas.bind('<space>', lambda _: self.start_game())
def add_ball(self):
if self.ball is not None:
self.ball.delete()
paddle_coords = self.paddle.get_position()
x = (paddle_coords[0] + paddle_coords[2]) * 0.5
self.ball = Ball(self.canvas, x, 310)
self.paddle.set_ball(self.ball)
def add_brick(self, x, y, hits):
brick = Brick(self.canvas, x, y, hits)
self.items[brick.item] = brick
def draw_text(self, x, y, text, size='40'):
font = ('Forte', size)
return self.canvas.create_text(x, y, text=text,
font=font)
def update_lives_text(self):
text = 'Lives: %s' % self.lives
if self.hud is None:
self.hud = self.draw_text(50, 20, text, 15)
else:
self.canvas.itemconfig(self.hud, text=text)
def start_game(self):
self.canvas.unbind('<space>')
self.canvas.delete(self.text)
self.paddle.ball = None
self.game_loop()
def game_loop(self):
self.check_collisions()
num_bricks = len(self.canvas.find_withtag('brick'))
if num_bricks == 0:
self.ball.speed = None
self.draw_text(300, 200, 'You win! You the Breaker of Bricks.')
elif self.ball.get_position()[3] >= self.height:
self.ball.speed = None
self.lives -= 1
if self.lives < 0:
self.draw_text(300, 200, 'You Lose! Game Over!')
else:
self.after(1000, self.setup_game)
else:
self.ball.update()
self.after(50, self.game_loop)
def check_collisions(self):
ball_coords = self.ball.get_position()
items = self.canvas.find_overlapping(*ball_coords)
objects = [self.items[x] for x in items if x in self.items]
self.ball.collide(objects)
if __name__ == '__main__':
root = tk.Tk()
root.title('Break those Bricks!')
game = Game(root)
game.mainloop()
To run the game, you must have Tkinter module installed. Save the code given above in a file with some name, let's say brick-breaker.py file. Then to run the code execute the following command:
python brick-breaker.py
Brick Breaker Game UI:
Here are a few snapshots of how the game looks.
When the player wins the game:
When the player loses the game:
And with that, this tutorial ends. Hope the game works fine for you. We recommend you to edit and customize the code to make the game better. Try to add more features to the game.