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?
Our aim is to add the bouncing ball to our Breakout game. To do so we will create a new class called Ball.
Bouncing Algorithm
To understand how to implement a bouncing algorithm, it is essential to understand how the computer controls the trajectory of a sprite (e.g. ball) on the screen. Arcade games are based on a frame based animation where the screen is refreshed every x milliseconds. Moving sprites are positionned using (x,y) coordinates and have a velocity vector (Vx,Vy) which specifies the delta in pixels to apply to the (x,y) coordinates of a sprite between two frames:
- frame n: Sprite Coordinates: (x,y)
- frame n+1: Sprite Coordinates: (x+Vx,y+Vy)
As the sprite moves across the screen, it may need to bounce against another sprite or against the edge of the screen.
Let’s investigate how the velocity vector is affected when the sprite bounces against vertical and horizontal walls/edges.
Ball Class
Below is the code for the Ball class. You will need to copy this code in a new Python file called ball.py. The update() method of this class will be called for each frame of the main program loop. It moves (changes the (x,y) coordinates of) the ball using its velocity vector.
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]
Adding the ball to the game
In the main.py file, we will first import the Ball class. (See line 5) We will then create an object called ball using the Ball class. (See lines 33 to 36) We will add this object to the all_sprites_list group of sprites. (See line 40)
We will also apply the bouncing algorithm to check if it needs to bounce against any of the four walls. (See lines 65 to 73)
#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 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 # Add the paddle 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] if ball.rect.y<40: ball.velocity[1] = -ball.velocity[1] # --- 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()
Collision Detection
The next addition to our game is to detect when the ball hits/collides with one the two paddles. If it does, we will make it bounce using a random new direction.
So first, let’s add a new method called bounce() to our Ball class.
Then, in the main program loop, let’s add some code to detect if the ball sprite collides with the paddleA or paddleB sprites. If it does we will call the bounce() method of the Ball class.
import pygame from random import randint BLACK = (0, 0, 0) class Ball(pygame.sprite.Sprite): #This class represents a car. 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 car, 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 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)
# 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 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 # 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 exit 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] 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() # --- 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()
Next Step?
The final touch consists of adding a scoring system:
Breakout Tutorial using Pygame:Adding a Brick Wall