Brick Breaker is a 2D arcade video game developed in the 1990s. The game consists of a paddle/striker located at the bottom end of the screen, a ball, and many blocks above the striker. The basic theme of this game is to break the blocks with the ball using the striker. The score is calculated by the number of blocks broken and each time the player fails to hit the ball and if the number of lives is 0, then it’s a game over. So, this article covers how to create such a game in Object Oriented Programming style (OOP) in Python using the Pygame module.
Instructions
- There are two kinds of blocks – White and Green. The green blocks break with just a single hit. However, the white blocks break only after two hits.
- If the ball touches the bottom edge of the screen, a life is deducted and if the number of lives is greater than 0, then the ball is tossed from the bottom left corner of the screen
- But if the lives are 0, then the screen pauses and the player needs to press the space bar if they wish to restart the game
Controls
Controls in this game are simple:
- We just need to use the left/right arrows to move the paddle. The left arrow moves the paddle leftwards and the right arrow moves the paddle rightwards
- If all lives are lost, then the game is over. Press the space bar to restart the game
Stepwise Implementation
The implementation is divided into 6 parts:
- Initial Setup: Contains all the initializations and global variables needed
- Helper Functions: These functions are not part of any class and are used by the game manager
- Striker Class: Contains the methods needed to control the paddle/striker
- Block Class: Contains the methods needed to control the blocks
- Ball Class: Contains the methods needed to control the ball
- Game Manager: Controls the game flow and logic
Step 1: Initial Setup
Here, we initialize the module and define all the necessary global variables
import pygame
import random
pygame.init() # Dimensions of the screen WIDTH, HEIGHT = 600 , 500
# Colors BLACK = ( 0 , 0 , 0 )
WHITE = ( 255 , 255 , 255 )
GREEN = ( 0 , 255 , 0 )
RED = ( 255 , 0 , 0 )
font = pygame.font.Font( 'freesansbold.ttf' , 15 )
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption( "Block Breaker" )
# to control the frame rate clock = pygame.time.Clock()
FPS = 30
|
Step 2: Helper Functions
The following are the helper functions:
- collisionChecker(): This function takes in two rects as arguments and returns if they collide or not
- populateBlocks(): This function takes in the block width, block height, vertical gap between the blocks, and horizontal gap between the blocks as arguments and populates a list of random blocks that can be rendered on screen. It is called when the game is over
- gameOver(): This function is called after all the lives are gone and it stays in an infinite loop until the space bar is pressed (replay) or the quit is pressed (exit)
# Helper Functions # Function used to check collisions between any two entities def collisionChecker(rect, ball):
if pygame.Rect.colliderect(rect, ball):
return True
return False
# Function used to populate the blocks def populateBlocks(blockWidth, blockHeight, horizontalGap, verticalGap):
listOfBlocks = []
for i in range ( 0 , WIDTH, blockWidth + horizontalGap):
for j in range ( 0 , HEIGHT / / 2 , blockHeight + verticalGap):
listOfBlocks.append(Block(i, j, blockWidth,
blockHeight,
random.choice([WHITE, GREEN])))
return listOfBlocks
# Once all the lives are over, this function waits # until exit or space bar is pressed and does the # corresponding action def gameOver():
gameOver = True
while gameOver:
# Event handling
for event in pygame.event.get():
if event. type = = pygame.QUIT:
return False
if event. type = = pygame.KEYDOWN:
if event.key = = pygame.K_SPACE:
return True
|
Step 3: Striker Class
This class defines the following method:
- __init()__ : Initializes the basic parameters of the paddle
Parameters:
- posx, posy => Position of the paddle with respect to the X and Y axes respectively
- width, height => Width and Height of the paddle
- speed => The speed with which the paddle moves when the left/right arrow keys are pressed
- color => Color of the paddle
- striker => A rectangle that is rendered on the screen
- strikerRect => rect of the striker that controls its position and collision
- display(): Used to display the paddle on the screen
- update(): Used to update the state of the paddle
- getRect(): Returns the rect variable of the paddle
# Striker class class Striker:
def __init__( self , posx, posy, width,
height, speed, color):
self .posx, self .posy = posx, posy
self .width, self .height = width, height
self .speed = speed
self .color = color
# The rect variable is used to handle the
# placement and the collisions of the object
self .strikerRect = pygame.Rect(
self .posx, self .posy, self .width, self .height)
self .striker = pygame.draw.rect(screen,
self .color, self .strikerRect)
# Used to render the object on the screen
def display( self ):
self .striker = pygame.draw.rect(screen,
self .color, self .strikerRect)
# Used to update the state of the object
def update( self , xFac):
self .posx + = self .speed * xFac
# Restricting the striker to be in between
# the left and right edges of the screen
if self .posx < = 0 :
self .posx = 0
elif self .posx + self .width > = WIDTH:
self .posx = WIDTH - self .width
self .strikerRect = pygame.Rect(
self .posx, self .posy, self .width, self .height)
# Returns the rect of the object
def getRect( self ):
return self .strikerRect
|
Step 4: Block Class
This class defines the following methods:
- __init__(): Initializes the basic parameters of the block
Parameters:
- posx, posy => Position of the block with respect to the X and Y axes respectively
- width, height => Width and Height of the block
- color => Color of the block. Its either white or green
- damage => Damage dealt to the block when the ball hits
- health => White block has a health of 200 and Green block has a health of 100
- block => A rectangle that is rendered on the screen
- blockRect => rect of the block that controls its position and collision
- display(): Used to display the block on the screen
- hit(): Used to reduce the block’s HP whenever it is being hit by the ball
- getRect(): Returns the rect variable of the block
- getHealth(): Returns the health of the block
# Block Class class Block:
def __init__( self , posx, posy, width, height, color):
self .posx, self .posy = posx, posy
self .width, self .height = width, height
self .color = color
self .damage = 100
# The white blocks have the health of 200.
# So, the ball must hit it twice to break
if color = = WHITE:
self .health = 200
else :
self .health = 100
# The rect variable is used to handle the placement
# and the collisions of the object
self .blockRect = pygame.Rect(
self .posx, self .posy, self .width, self .height)
self .block = pygame.draw.rect(screen,
self .color, self .blockRect)
# Used to render the object on the screen if and
# only if its health is greater than 0
def display( self ):
if self .health > 0 :
self .brick = pygame.draw.rect(screen,
self .color, self .blockRect)
# Used to decrease the health of the block
def hit( self ):
self .health - = self .damage
# Used to get the rect of the object
def getRect( self ):
return self .blockRect
# Used to get the health of the object
def getHealth( self ):
return self .health
|
Step 5: Ball Class
The ball class defines the following methods:
- __init__(): Defines the basic parameters of the ball
Parameters
- posx, posy => Position of the ball with respect to the X and Y axes respectively
- radius => Radius of the ball
- color => Color of the ball
- xFac => Determines the direction of the ball along the X axis. Negative value indicates its moving leftwards and positive value indicates its moving rightwards
- yFac => Determines the direction of the ball along the Y axis. Negative value indicates its moving upwards and positive value indicates its moving downards
- ball => A circle that is rendered on the screen. It is also used to control the position and check for collisions
- display(): Used to display the ball on the screen
- update(): Used to update the state of the ball
- reset(): Whenever a life is lost, this method is used to get the ball back to its initial position
- hit(): Used to change the direction of the ball whenever it is hit
- getRect(): Returns the rect variable of the ball
# Ball Class class Ball:
def __init__( self , posx, posy, radius, speed, color):
self .posx, self .posy = posx, posy
self .radius = radius
self .speed = speed
self .color = color
self .xFac, self .yFac = 1 , 1
self .ball = pygame.draw.circle(screen, self .color, ( self .posx, self .posy), self .radius)
# Used to display the object on the screen
def display( self ):
self .ball = pygame.draw.circle(screen, self .color, ( self .posx, self .posy), self .radius)
# Used to update the state of the object
def update( self ):
self .posx + = self .xFac * self .speed
self .posy + = self .yFac * self .speed
# Reflecting the ball if it touches either of the vertical edges
if self .posx < = 0 or self .posx > = WIDTH:
self .xFac * = - 1
# Reflection from the top most edge of the screen
if self .posy < = 0 :
self .yFac * = - 1
# If the ball touches the bottom most edge of the screen, True value is returned
if self .posy > = HEIGHT:
return True
return False
# Resets the position of the ball
def reset( self ):
self .posx = 0
self .posy = HEIGHT
self .xFac, self .yFac = 1 , - 1
# Used to change the direction along Y axis
def hit( self ):
self .yFac * = - 1
# Returns the rect of the ball. In this case, it is the ball itself
def getRect( self ):
return self .ball
|
Step 6: Game Manager
The game manager takes care of the game flow and implements the game logic.
# Game Manager def main():
running = True
lives = 3
score = 0
scoreText = font.render( "score" , True , WHITE)
scoreTextRect = scoreText.get_rect()
scoreTextRect.center = ( 20 , HEIGHT - 10 )
livesText = font.render( "Lives" , True , WHITE)
livesTextRect = livesText.get_rect()
livesTextRect.center = ( 120 , HEIGHT - 10 )
striker = Striker( 0 , HEIGHT - 50 , 100 , 20 , 10 , WHITE)
strikerXFac = 0
ball = Ball( 0 , HEIGHT - 150 , 7 , 5 , WHITE)
blockWidth, blockHeight = 40 , 15
horizontalGap, verticalGap = 20 , 20
listOfBlocks = populateBlocks(
blockWidth, blockHeight, horizontalGap, verticalGap)
# Game loop
while running:
screen.fill(BLACK)
screen.blit(scoreText, scoreTextRect)
screen.blit(livesText, livesTextRect)
scoreText = font.render( "Score : " + str (score), True , WHITE)
livesText = font.render( "Lives : " + str (lives), True , WHITE)
# If all the blocks are destroyed, then we repopulate them
if not listOfBlocks:
listOfBlocks = populateBlocks(
blockWidth, blockHeight, horizontalGap, verticalGap)
# All the lives are over. So, the gameOver() function is called
if lives < = 0 :
running = gameOver()
while listOfBlocks:
listOfBlocks.pop( 0 )
lives = 3
score = 0
listOfBlocks = populateBlocks(
blockWidth, blockHeight, horizontalGap, verticalGap)
# Event handling
for event in pygame.event.get():
if event. type = = pygame.QUIT:
running = False
if event. type = = pygame.KEYDOWN:
if event.key = = pygame.K_LEFT:
strikerXFac = - 1
if event.key = = pygame.K_RIGHT:
strikerXFac = 1
if event. type = = pygame.KEYUP:
if event.key = = pygame.K_LEFT or event.key = = pygame.K_RIGHT:
strikerXFac = 0
# Collision check
if (collisionChecker(striker.getRect(), ball.getRect())):
ball.hit()
for block in listOfBlocks:
if (collisionChecker(block.getRect(), ball.getRect())):
ball.hit()
block.hit()
if block.getHealth() < = 0 :
listOfBlocks.pop(listOfBlocks.index(block))
score + = 5
# Update
striker.update(strikerXFac)
lifeLost = ball.update()
if lifeLost:
lives - = 1
ball.reset()
print (lives)
# Display
striker.display()
ball.display()
for block in listOfBlocks:
block.display()
pygame.display.update()
clock.tick(FPS)
# This code is contributed by teja00219 |
Complete Code:
By joining all the above classes, helper functions, and the game manager together, the final code would look like this.
import pygame
import random
pygame.init() # Dimensions of the screen WIDTH, HEIGHT = 600 , 500
# Colors BLACK = ( 0 , 0 , 0 )
WHITE = ( 255 , 255 , 255 )
GREEN = ( 0 , 255 , 0 )
RED = ( 255 , 0 , 0 )
font = pygame.font.Font( 'freesansbold.ttf' , 15 )
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption( "Block Breaker" )
# to control the frame rate clock = pygame.time.Clock()
FPS = 30
# Striker class class Striker:
def __init__( self , posx, posy, width, height, speed, color):
self .posx, self .posy = posx, posy
self .width, self .height = width, height
self .speed = speed
self .color = color
# The rect variable is used to handle the placement
# and the collisions of the object
self .strikerRect = pygame.Rect(
self .posx, self .posy, self .width, self .height)
self .striker = pygame.draw.rect(screen,
self .color, self .strikerRect)
# Used to render the object on the screen
def display( self ):
self .striker = pygame.draw.rect(screen,
self .color, self .strikerRect)
# Used to update the state of the object
def update( self , xFac):
self .posx + = self .speed * xFac
# Restricting the striker to be in between the
# left and right edges of the screen
if self .posx < = 0 :
self .posx = 0
elif self .posx + self .width > = WIDTH:
self .posx = WIDTH - self .width
self .strikerRect = pygame.Rect(
self .posx, self .posy, self .width, self .height)
# Returns the rect of the object
def getRect( self ):
return self .strikerRect
# Block Class class Block:
def __init__( self , posx, posy, width, height, color):
self .posx, self .posy = posx, posy
self .width, self .height = width, height
self .color = color
self .damage = 100
# The white blocks have the health of 200. So,
# the ball must hit it twice to break
if color = = WHITE:
self .health = 200
else :
self .health = 100
# The rect variable is used to handle the placement
# and the collisions of the object
self .blockRect = pygame.Rect(
self .posx, self .posy, self .width, self .height)
self .block = pygame.draw.rect(screen, self .color,
self .blockRect)
# Used to render the object on the screen if and only
# if its health is greater than 0
def display( self ):
if self .health > 0 :
self .brick = pygame.draw.rect(screen,
self .color, self .blockRect)
# Used to decrease the health of the block
def hit( self ):
self .health - = self .damage
# Used to get the rect of the object
def getRect( self ):
return self .blockRect
# Used to get the health of the object
def getHealth( self ):
return self .health
# Ball Class class Ball:
def __init__( self , posx, posy, radius, speed, color):
self .posx, self .posy = posx, posy
self .radius = radius
self .speed = speed
self .color = color
self .xFac, self .yFac = 1 , 1
self .ball = pygame.draw.circle(
screen, self .color, ( self .posx,
self .posy), self .radius)
# Used to display the object on the screen
def display( self ):
self .ball = pygame.draw.circle(
screen, self .color, ( self .posx,
self .posy), self .radius)
# Used to update the state of the object
def update( self ):
self .posx + = self .xFac * self .speed
self .posy + = self .yFac * self .speed
# Reflecting the ball if it touches
# either of the vertical edges
if self .posx < = 0 or self .posx > = WIDTH:
self .xFac * = - 1
# Reflection from the top most edge of the screen
if self .posy < = 0 :
self .yFac * = - 1
# If the ball touches the bottom most edge of
# the screen, True value is returned
if self .posy > = HEIGHT:
return True
return False
# Resets the position of the ball
def reset( self ):
self .posx = 0
self .posy = HEIGHT
self .xFac, self .yFac = 1 , - 1
# Used to change the direction along Y axis
def hit( self ):
self .yFac * = - 1
# Returns the rect of the ball. In this case,
# it is the ball itself
def getRect( self ):
return self .ball
# Helper Functions # Function used to check collisions between any two entities def collisionChecker(rect, ball):
if pygame.Rect.colliderect(rect, ball):
return True
return False
# Function used to populate the blocks def populateBlocks(blockWidth, blockHeight,
horizontalGap, verticalGap):
listOfBlocks = []
for i in range ( 0 , WIDTH, blockWidth + horizontalGap):
for j in range ( 0 , HEIGHT / / 2 , blockHeight + verticalGap):
listOfBlocks.append(
Block(i, j, blockWidth, blockHeight,
random.choice([WHITE, GREEN])))
return listOfBlocks
# Once all the lives are over, this function waits until # exit or space bar is pressed and does the corresponding action def gameOver():
gameOver = True
while gameOver:
# Event handling
for event in pygame.event.get():
if event. type = = pygame.QUIT:
return False
if event. type = = pygame.KEYDOWN:
if event.key = = pygame.K_SPACE:
return True
# Game Manager def main():
running = True
lives = 3
score = 0
scoreText = font.render( "score" , True , WHITE)
scoreTextRect = scoreText.get_rect()
scoreTextRect.center = ( 20 , HEIGHT - 10 )
livesText = font.render( "Lives" , True , WHITE)
livesTextRect = livesText.get_rect()
livesTextRect.center = ( 120 , HEIGHT - 10 )
striker = Striker( 0 , HEIGHT - 50 , 100 , 20 , 10 , WHITE)
strikerXFac = 0
ball = Ball( 0 , HEIGHT - 150 , 7 , 5 , WHITE)
blockWidth, blockHeight = 40 , 15
horizontalGap, verticalGap = 20 , 20
listOfBlocks = populateBlocks(
blockWidth, blockHeight, horizontalGap, verticalGap)
# Game loop
while running:
screen.fill(BLACK)
screen.blit(scoreText, scoreTextRect)
screen.blit(livesText, livesTextRect)
scoreText = font.render( "Score : " + str (score), True , WHITE)
livesText = font.render( "Lives : " + str (lives), True , WHITE)
# If all the blocks are destroyed, then we repopulate them
if not listOfBlocks:
listOfBlocks = populateBlocks(
blockWidth, blockHeight, horizontalGap, verticalGap)
# All the lives are over. So, the gameOver() function is called
if lives < = 0 :
running = gameOver()
while listOfBlocks:
listOfBlocks.pop( 0 )
lives = 3
score = 0
listOfBlocks = populateBlocks(
blockWidth, blockHeight, horizontalGap, verticalGap)
# Event handling
for event in pygame.event.get():
if event. type = = pygame.QUIT:
running = False
if event. type = = pygame.KEYDOWN:
if event.key = = pygame.K_LEFT:
strikerXFac = - 1
if event.key = = pygame.K_RIGHT:
strikerXFac = 1
if event. type = = pygame.KEYUP:
if event.key = = pygame.K_LEFT or event.key = = pygame.K_RIGHT:
strikerXFac = 0
# Collision check
if (collisionChecker(striker.getRect(),
ball.getRect())):
ball.hit()
for block in listOfBlocks:
if (collisionChecker(block.getRect(), ball.getRect())):
ball.hit()
block.hit()
if block.getHealth() < = 0 :
listOfBlocks.pop(listOfBlocks.index(block))
score + = 5
# Update
striker.update(strikerXFac)
lifeLost = ball.update()
if lifeLost:
lives - = 1
ball.reset()
print (lives)
# Display
striker.display()
ball.display()
for block in listOfBlocks:
block.display()
pygame.display.update()
clock.tick(FPS)
if __name__ = = "__main__" :
main()
pygame.quit()
|
Output: