Flappy bird was originally released as a mobile game where you tap the screen to make the bird fly. If the bird hits the pipes or the edges of the screen, the game ends and the player needs to restart.
In this tutorial, we have made a computer version of the game where the bird will be controlled using the up key or the space bar.
We will be using Python language for writing the code. We will also be using Pygame which is a cross-platform set of Python modules designed for writing video games.
It includes computer graphics and sound libraries designed to be used with the Python programming language. Pygame is suitable to create client-side applications that can be potentially wrapped in a standalone executable.
So, prior knowledge of python and Pygame is required for this project.
Building Flappy Bird Game in Python
1. Importing Modules
For the project, we are importing the necessary modules. We will use random
for generating random numbers for our game. sys.exit
from the sys
module will be used to exit the program. In line 3 and 4 we are importing Pygame and the basic Pygame imports respectively.
import random
import sys
import pygame
from pygame.locals import *
2. Global Variables Declared
In this step, we are declaring various global
variables for our game. We first set the value for fps
(frames per second), screen_width and screen_height.
We create the screen with screen_width
and screen_height
as an argument for the pygame.display.set_mode()
function. Then, we create a ground-y variable which will give the y-coordinate for our base image, and 2 dictionaries game_images and game_sounds which will contain our various images and sounds used for the game.
Then, we store the images of the player (bird), background, pipe and the title in these variables by giving their paths.
fps = 32
screen_width = 289
screen_height = 511
screen = pygame.display.set_mode((screen_width,screen_height))
ground_y = screen_height*0.8
game_images = {}
game_sounds = {}
player = 'gallery/images/bird.png'
background = 'gallery/images/background.png'
pipe = 'gallery/images/pipe.png'
title = 'gallery/images/title.png'


3. Creating the “__main__” function
Now let’s create the main function where our game will start and we have to initialize all pygame modules using pygame.init()
. We also create fps_clock
variable to help us track time at a moment using pygame.tick.Clock()
function.
Then we will give a title to our main game window and store all the images in a tuple with first, which we are then assigning to the ’numbers’ key in the game_images
dictionary. We use pygame.image.load()
with paths of the images as arguments along with convert_alpha()
to change the pixel format of an image including per pixel alphas.
Similarly, we add the images of the message, base, pipe, background, player, and title, into the dictionary using various keys. For pipe, we also added an inverted pipe image by using pygame.transform.rotate()
function and rotating the image by 180 degrees. We then add the sounds to the game_sounds
dictionary using various keys.
It is similar to what we did for images but here we use pygame.mixer.Sound()
function with the paths for various sounds as the argument for storing the sounds. Then we start a loop calling the welcomeScreen()
and mainGame()
functions which will be defined in the later sections.
if __name__ == "__main__":
pygame.init()
fps_clock = pygame.time.Clock()
pygame.display.set_caption('Flappy Bird')
game_images['numbers'] = (
pygame.image.load('gallery/images/0.png').convert_alpha(),
pygame.image.load('gallery/images/1.png').convert_alpha(),
pygame.image.load('gallery/images/2.png').convert_alpha(),
pygame.image.load('gallery/images/3.png').convert_alpha(),
pygame.image.load('gallery/images/4.png').convert_alpha(),
pygame.image.load('gallery/images/5.png').convert_alpha(),
pygame.image.load('gallery/images/6.png').convert_alpha(),
pygame.image.load('gallery/images/7.png').convert_alpha(),
pygame.image.load('gallery/images/8.png').convert_alpha(),
pygame.image.load('gallery/images/9.png').convert_alpha()
)
game_images['message'] = pygame.image.load('gallery/images/message.png').convert_alpha()
game_images['base'] = pygame.image.load('gallery/images/base.png').convert_alpha()
game_images['pipe'] = (
pygame.transform.rotate(pygame.image.load(pipe).convert_alpha(), 180),
pygame.image.load(pipe).convert_alpha()
)
game_images['background'] = pygame.image.load(background).convert_alpha()
game_images['player'] = pygame.image.load(player).convert_alpha()
game_images['title'] = pygame.image.load(title).convert_alpha()
#Game Sounds
game_sounds['die'] = pygame.mixer.Sound('gallery/audio/die.wav')
game_sounds['hit'] = pygame.mixer.Sound('gallery/audio/hit.wav')
game_sounds['point'] = pygame.mixer.Sound('gallery/audio/point.wav')
game_sounds['swoosh'] = pygame.mixer.Sound('gallery/audio/swoosh.wav')
game_sounds['wing'] = pygame.mixer.Sound('gallery/audio/wing.wav')
while True:
welcomeScreen()
mainGame()
4. Creating “welcomeScreen” function
Now, we define our welcomeScreen()
function which will display the welcome screen on starting the game. We start by assigning the values of the x-coordinate and y-coordinate for the player, message, and title images.
We have selected the arguments by hit and trial method and you can alter the values that suit you the best. We also give the x-coordinate of base here. Then, we start a while loop which will always be True and thus will start a loop that will not stop unless the control says quit.
Here we make use of a for loop for analyzing all the events taking place throughout the game using pygame.event.get()
function. Then we check that whenever a quit type of event is encountered by pressing the escape key, the game window will close.
We will check the next condition i.e. whether we clicked the up key or the space button. If yes, we will return from the function and start the game. And if no key or button is pressed, the welcome screen is displayed. For that, we will place the background, message, player, base, and title images using screen.blit()
function.
Finally, we will update our window using pygame.display.update()
and will update our clock variable with fps value as argument to show just 32 frames per second.
def welcomeScreen():
player_x = int(screen_width/8)
player_y = int((screen_height - game_images['player'].get_height())/2)
message_x = int((screen_width - game_images['message'].get_width())/2)
message_y = int(screen_height*0.2)
title_x = int((screen_width - game_images['message'].get_width())/2)
title_y = int(screen_height*0.04)
base_x = 0
while True:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
elif event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
return
else:
screen.blit(game_images['background'],(0,0))
screen.blit(game_images['message'],(message_x,message_y))
screen.blit(game_images['player'],(player_x,player_y))
screen.blit(game_images['base'],(base_x,ground_y))
screen.blit(game_images['title'],(title_x,title_y))
pygame.display.update()
fps_clock.tick(fps)
5. Creating the “mainGame()” function
Now we define our mainGame() function by first initializing the variable score with 0, and also give the coordinates for player image and base again.
Then we create 2 pipes for blitting on the screen using getRandomPipe()
which we will be defined later. Then we create a list of upper pipes (inverted ones) and lower pipes with their x and y coordinates.
Again we have chosen values by hit and trial method. Then, we declare variables for velocities in different directions for the bird. We also provide an acceleration variable.
playerFlapVel is the velocity while flapping and playerFlapped is set to false (which is true only if the bird flaps). Then again we check for events.
- First for exiting the game and exit the game if true.
- Then we check if the up key or spacebar is pressed. If yes, we check if the player is below the screen top and if yes, we make some updates and play the sound of the wing using .play().
- After this, we check if we have crashed using the isCollide() function we will define soon. If true, we will return from the function.
Then, we will check and update the scores. Using the player’s, mid position, and the positions of the pipes, we increase the score if we cross a pipe and print it in the console.
Also, we play the point sound for crossing each pipe. Then if the player velocity in y-direction has not yet become max, we will provide the acceleration.
Later on, we update the playerFlpped value and then the position of the bird. We move the pipes to the left and add a new pipe when the first one is about to cross the leftmost part of the screen.
We will also see if the pipe is out of the screen and if yes, we remove it and place our pipes and the score on our screen, later on, update the display screen.
For the score, we first access all the digits of the score (if more than 1 digit score) and place the required images. We update our clock again.
def mainGame():
score = 0
player_x = int(screen_width/8)
player_y = int(screen_height/2)
base_x = 0
newPipe1 = getRandomPipe()
newPipe2 = getRandomPipe()
upperPipes = [
{'x': screen_width+200, 'y': newPipe1[0]['y']},
{'x': screen_width+200+(screen_width/2), 'y': newPipe2[0]['y']}
]
lowerPipes = [
{'x': screen_width+200, 'y': newPipe1[1]['y']},
{'x': screen_width+200+(screen_width/2), 'y': newPipe2[1]['y']}
]
pipeVelX = -4
playerVelY = -9
playerMaxVelY = 10
playerMinVelY = -8
playerAccY = 1
playerFlapVel = -8
playerFlapped = False
while True:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
if player_y > 0:
playerVelY = playerFlapVel
playerFlapped = True
game_sounds['wing'].play()
crashTest = isCollide(player_x, player_y, upperPipes, lowerPipes)
if crashTest:
return
playerMidPos = player_x + game_images['player'].get_width()/2
for pipe in upperPipes:
pipeMidPos = pipe['x'] + game_images['pipe'][0].get_width()/2
if pipeMidPos<= playerMidPos < pipeMidPos + 4:
score +=1
print(f"Your Score is {score}")
game_sounds['point'].play()
if playerVelY <playerMaxVelY and not playerFlapped:
playerVelY += playerAccY
if playerFlapped:
playerFlapped = False
playerHeight = game_images['player'].get_height()
player_y = player_y + min(playerVelY, ground_y - player_y - playerHeight)
for upperPipe, lowerPipe in zip(upperPipes, lowerPipes):
upperPipe['x'] += pipeVelX
lowerPipe['x'] += pipeVelX
if 0<upperPipes[0]['x']<5:
newPipe = getRandomPipe()
upperPipes.append(newPipe[0])
lowerPipes.append(newPipe[1])
if upperPipes[0]['x'] < -game_images['pipe'][0].get_width():
upperPipes.pop(0)
lowerPipes.pop(0)
screen.blit(game_images['background'], (0, 0))
for upperPipe, lowerPipe in zip(upperPipes, lowerPipes):
screen.blit(game_images['pipe'][0], (upperPipe['x'], upperPipe['y']))
screen.blit(game_images['pipe'][1], (lowerPipe['x'], lowerPipe['y']))
screen.blit(game_images['base'], (base_x, ground_y))
screen.blit(game_images['player'], (player_x, player_y))
myDigits = [int(x) for x in list(str(score))]
width = 0
for digit in myDigits:
width += game_images['numbers'][digit].get_width()
Xoffset = (screen_width - width)/2
for digit in myDigits:
screen.blit(game_images['numbers'][digit], (Xoffset, screen_height*0.12))
Xoffset += game_images['numbers'][digit].get_width()
pygame.display.update()
fps_clock.tick(fps)
6. isCollide() and getRandomPipe() functions
In the isCollide() function, first, we check if we have hit the top of the base inline and then we look for collision with upper pipes by comparing the position of the bird with that of the pipe to check for the collision.
We repeat the same for lower pipes. If any of the collision conditions are true, we play the hit sound and return True.
In the getRandomPipe() function, we store the height of the pipe in the pipeHeight variable and use the offset variable to store one-third of screen_width.
We then assign the values of the x and y coordinates for the pipes using random functions at equal distances, but with different sizes of upper and lower pipes. Then we store the coordinates in a list named pipe and return it.
def isCollide(player_x, player_y, upperPipes, lowerPipes):
if player_y>ground_y-25 or player_y<0:
game_sounds['hit'].play()
return True
for pipe in upperPipes:
pipeHeight = game_images['pipe'][0].get_height()
if (player_y < pipeHeight + pipe['y']) and (abs(player_x - pipe['x']) < game_images['pipe'][0].get_width() - 15):
game_sounds['hit'].play()
return True
for pipe in lowerPipes:
if (player_y + game_images['player'].get_height() > pipe['y']) and (abs(player_x - pipe['x']) < game_images['pipe'][0].get_width() - 15):
game_sounds['hit'].play()
return True
return False
def getRandomPipe():
pipeHeight = game_images['pipe'][0].get_height()
offset = screen_height/3
y2 = offset + random.randrange(0, int(screen_height - game_images['base'].get_height() - 1.2*offset))
pipeX = screen_width + 10
y1 = pipeHeight - y2 + offset
pipe = [
{'x': pipeX, 'y': -y1},
{'x': pipeX, 'y': y2}
]
return pipe
The Final Output
The video below demonstrates the final output of the final flappy bird game!
Conclusion
Congratulations! Today we build our own flappy bird game right from scratch!
Hope you like it! Thank you for reading!