By the end of this week, we'll hopefully have a functional Flappy Bird game that tracks how many pipes have passed and ends the game when a bird hits a pipe.

Pipes

Step 1: Creating a Pipe

  1. Create a file in the classes directory called pipe.py that will hold our Pipe class.
  2. We’ll define three class variables: the two images and an array that will hold all of the Pipe objects currently in the game.
  3. Constructor:
    1. Initialize the pipe rects (one for the bottom pipe and one for the top pipe).
    2. Set the velocity of the pipe (this will be how fast they travel across the screen).
    3. Append this Pipe to the array of pipes.

  1. Update: Move the pipes and if the current pipe moves out of the screen (past x = 0), then remove it.
  2. Draw: Similar to other objects we’ve created before.
import pygame
import os

class Pipe:
pipes = []

bottom_pipe_image = pygame.image.load(os.path.join("../assets/pipe", "pipe-bottom.png"))
top_pipe_image = pygame.image.load(os.path.join("../assets/pipe", "pipe-top.png"))

def __init__(self, x, bottom_y, top_y):
   self.bottom_pipe_rect = pygame.Rect(x, bottom_y, self.bottom_pipe_image.get_width(), self.bottom_pipe_image.get_height())
   self.top_pipe_rect = pygame.Rect(x, top_y - self.top_pipe_image.get_height(), self.top_pipe_image.get_width(), self.top_pipe_image.get_height())
   self.v = 180
   self.pipes.append(self)

def update(self, dt):
   self.bottom_pipe_rect.x -= self.v * dt
   self.top_pipe_rect.x -= self.v * dt
   if self.bottom_pipe_rect.x < -self.bottom_pipe_image.get_width():
       self.pipes.remove(self)

def draw(self, screen):
   # Bottom pipe
   screen.blit(self.bottom_pipe_image, (self.bottom_pipe_rect.x, self.bottom_pipe_rect.y))
  
   # Top pipe
   screen.blit(self.top_pipe_image, (self.top_pipe_rect.x, self.top_pipe_rect.y))

Step 2: Add pipes to our game loop

  1. For now, let’s only keep one pipe on the screen at a time. So, if there are no pipes, randomly choose values for bottom_y, top_y and initialize a pipe.
  2. Update and draw like we’ve been doing up to this point.
def main():
   FPS = 60

   run = True

   # Define a Pygame clock
   clock = pygame.time.Clock()

   # Initialize the background
   bg = Background(SCREEN_WIDTH, SCREEN_HEIGHT)

   # Initialize birds
   Bird.birds = [Bird(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, "yellow")]
  
   while run:
       # Event handling
       for event in pygame.event.get():
           if event.type == pygame.QUIT:
               run = False
               pygame.quit()
           if event.type == pygame.KEYDOWN:
               if event.key == pygame.K_SPACE:
                   for bird in Bird.birds:
                       bird.jump()

       **if len(Pipe.pipes) == 0:
           bottom_y = random.randint(300, SCREEN_HEIGHT - 200)
           top_y = random.randint(100, bottom_y - 200)
           pipe = Pipe(SCREEN_WIDTH, bottom_y, top_y)**

       # Updating and drawing
       dt = 1 / 60
       SCREEN.fill((255, 255, 255)) # Clear background
       bg.draw(SCREEN)
       bg.update(dt)

       **for pipe in Pipe.pipes:
           pipe.update(dt)
           pipe.draw(SCREEN)**

       for bird in Bird.birds:
           bird.update(dt)
           bird.draw(SCREEN)

       pygame.display.update()
       clock.tick(FPS)

Collisions

Step 1: We want our bird to collide with a pipe if one of the pixels in the bird ever touches a pixel in a pipe. There are a couple ways to code this in Pygame, for example using the Rect objects of the Bird and the Pipes (see below). But, because we want our collisions to be as pixel-perfect as possible (and for other reasons we'll see later), we are going to implement this using masks.

[https://repl.it/@Rabbid76/PyGame-colliderect#main.py](https://lh6.googleusercontent.com/ojh5HwBITX848qhuJXY6lQq_i-Bvke2C-Uw7MQ1-DXFWY6AhflxWR9hCTZpOdYmJsyfjUwFAvmAToRntQPlTsfCPkEzyFFY6bnTW-kPM8XuR411AlCS-vt3jEzJVlhPmto8KPIb2=s0)

https://repl.it/@Rabbid76/PyGame-colliderect#main.py

<aside> 💡 Masks in Pygame are short for bitmasks, meaning all bits of opaque pixels are set to 1 and any transparent pixels are 0. We then check if the bird and pipe masks overlap at any point to see if a collision occurs.

</aside>

  1. Add a get_mask method to bird and a collide method to pipe.py.
  2. Calling the method from_surface returns a Mask object for the Bird.