This tutorial is the second tutorial in a series of five Pygame tutorials:
- Breakout Tutorial 1: Getting Started
- Breakout Tutorial 2: Adding the Paddle
- Breakout Tutorial 3: Controlling the Paddle
- Breakout Tutorial 4: Adding a Bouncing Ball
- Breakout Tutorial 5: Adding a Brick Wall
- Extra: Pygame How To’s?
The final stage of our tutorial focuses on adding a brick wall and a scoring system to our Breakout game:
- The player will score a point if the ball bounces against a brick.
- The player will lose a life if the ball bounces against the bottom edge of the screen.
- Both the score and number of lives will be displayed at the top of the screen.
- A “Level Complete” message will be displayed if all bricks have been removed.
The final code for the main.py is provided below. We made several changes to the code as follows:
- On line 6 we import the Brick class. (Code provided in the brick.py tab)
- On lines 39 to 57 we create three rows of bricks and add them to a group called all_bricks.
- On lines 93 to 103 we take a life away when the ball hit the bottom edge of the screen. If the number of lives reaches zero, we display a “Game Over” message..
- On lines 114 to 129 we detect if the ball hits a brick. If so we remove the brick (using the kill() method) and increment the score by one.
main.pypaddle.pyball.pybrick.py
#Import the pygame library and initialise the game engine import pygame #Let's import the Paddle Class & the Ball Class from paddle import Paddle from ball import Ball from brick import Brick pygame.init() # Define some colors WHITE = (255,255,255) DARKBLUE = (36,90,190) LIGHTBLUE = (0,176,240) RED = (255,0,0) ORANGE = (255,100,0) YELLOW = (255,255,0) score = 0 lives = 3 # Open a new window size = (800, 600) screen = pygame.display.set_mode(size) pygame.display.set_caption("Breakout Game") #This will be a list that will contain all the sprites we intend to use in our game. all_sprites_list = pygame.sprite.Group() #Create the Paddle paddle = Paddle(LIGHTBLUE, 100, 10) paddle.rect.x = 350 paddle.rect.y = 560 #Create the ball sprite ball = Ball(WHITE,10,10) ball.rect.x = 345 ball.rect.y = 195 all_bricks = pygame.sprite.Group() for i in range(7): brick = Brick(RED,80,30) brick.rect.x = 60 + i* 100 brick.rect.y = 60 all_sprites_list.add(brick) all_bricks.add(brick) for i in range(7): brick = Brick(ORANGE,80,30) brick.rect.x = 60 + i* 100 brick.rect.y = 100 all_sprites_list.add(brick) all_bricks.add(brick) for i in range(7): brick = Brick(YELLOW,80,30) brick.rect.x = 60 + i* 100 brick.rect.y = 140 all_sprites_list.add(brick) all_bricks.add(brick) # Add the paddle and the ball to the list of sprites all_sprites_list.add(paddle) all_sprites_list.add(ball) # The loop will carry on until the user exits the game (e.g. clicks the close button). carryOn = True # The clock will be used to control how fast the screen updates clock = pygame.time.Clock() # -------- Main Program Loop ----------- while carryOn: # --- Main event loop for event in pygame.event.get(): # User did something if event.type == pygame.QUIT: # If user clicked close carryOn = False # Flag that we are done so we exit this loop #Moving the paddle when the use uses the arrow keys keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: paddle.moveLeft(5) if keys[pygame.K_RIGHT]: paddle.moveRight(5) # --- Game logic should go here all_sprites_list.update() #Check if the ball is bouncing against any of the 4 walls: if ball.rect.x>=790: ball.velocity[0] = -ball.velocity[0] if ball.rect.x<=0: ball.velocity[0] = -ball.velocity[0] if ball.rect.y>590: ball.velocity[1] = -ball.velocity[1] lives -= 1 if lives == 0: #Display Game Over Message for 3 seconds font = pygame.font.Font(None, 74) text = font.render("GAME OVER", 1, WHITE) screen.blit(text, (250,300)) pygame.display.flip() pygame.time.wait(3000) #Stop the Game carryOn=False if ball.rect.y<40: ball.velocity[1] = -ball.velocity[1] #Detect collisions between the ball and the paddles if pygame.sprite.collide_mask(ball, paddle): ball.rect.x -= ball.velocity[0] ball.rect.y -= ball.velocity[1] ball.bounce() #Check if there is the ball collides with any of bricks brick_collision_list = pygame.sprite.spritecollide(ball,all_bricks,False) for brick in brick_collision_list: ball.bounce() score += 1 brick.kill() if len(all_bricks)==0: #Display Level Complete Message for 3 seconds font = pygame.font.Font(None, 74) text = font.render("LEVEL COMPLETE", 1, WHITE) screen.blit(text, (200,300)) pygame.display.flip() pygame.time.wait(3000) #Stop the Game carryOn=False # --- Drawing code should go here # First, clear the screen to dark blue. screen.fill(DARKBLUE) pygame.draw.line(screen, WHITE, [0, 38], [800, 38], 2) #Display the score and the number of lives at the top of the screen font = pygame.font.Font(None, 34) text = font.render("Score: " + str(score), 1, WHITE) screen.blit(text, (20,10)) text = font.render("Lives: " + str(lives), 1, WHITE) screen.blit(text, (650,10)) #Now let's draw all the sprites in one go. (For now we only have 2 sprites!) all_sprites_list.draw(screen) # --- Go ahead and update the screen with what we've drawn. pygame.display.flip() # --- Limit to 60 frames per second clock.tick(60) #Once we have exited the main program loop we can stop the game engine: pygame.quit()
The code for the Paddle class remains unchanged.
import pygame BLACK = (0,0,0) class Paddle(pygame.sprite.Sprite): #This class represents a paddle. It derives from the "Sprite" class in Pygame. def __init__(self, color, width, height): # Call the parent class (Sprite) constructor super().__init__() # Pass in the color of the paddle, its width and height. # Set the background color and set it to be transparent self.image = pygame.Surface([width, height]) self.image.fill(BLACK) self.image.set_colorkey(BLACK) # Draw the paddle (a rectangle!) pygame.draw.rect(self.image, color, [0, 0, width, height]) # Fetch the rectangle object that has the dimensions of the image. self.rect = self.image.get_rect() def moveLeft(self, pixels): self.rect.x -= pixels #Check that you are not going too far (off the screen) if self.rect.x < 0: self.rect.x = 0 def moveRight(self, pixels): self.rect.x += pixels #Check that you are not going too far (off the screen) if self.rect.x > 700: self.rect.x = 700
The code for the Ball class remains unchanged.
import pygame from random import randint BLACK = (0, 0, 0) class Ball(pygame.sprite.Sprite): #This class represents a ball. It derives from the "Sprite" class in Pygame. def __init__(self, color, width, height): # Call the parent class (Sprite) constructor super().__init__() # Pass in the color of the ball, its width and height. # Set the background color and set it to be transparent self.image = pygame.Surface([width, height]) self.image.fill(BLACK) self.image.set_colorkey(BLACK) # Draw the ball (a rectangle!) pygame.draw.rect(self.image, color, [0, 0, width, height]) self.velocity = [randint(4,8),randint(-8,8)] # Fetch the rectangle object that has the dimensions of the image. self.rect = self.image.get_rect() def update(self): self.rect.x += self.velocity[0] self.rect.y += self.velocity[1] def bounce(self): self.velocity[0] = -self.velocity[0] self.velocity[1] = randint(-8,8)
This is the code for the new Brick class, to be saved in a new file called brick.py.
import pygame BLACK = (0,0,0) class Brick(pygame.sprite.Sprite): #This class represents a brick. It derives from the "Sprite" class in Pygame. def __init__(self, color, width, height): # Call the parent class (Sprite) constructor super().__init__() # Pass in the color of the brick, and its x and y position, width and height. # Set the background color and set it to be transparent self.image = pygame.Surface([width, height]) self.image.fill(BLACK) self.image.set_colorkey(BLACK) # Draw the brick (a rectangle!) pygame.draw.rect(self.image, color, [0, 0, width, height]) # Fetch the rectangle object that has the dimensions of the image. self.rect = self.image.get_rect()