Create a Flappy Bird Clone With Python P9
Posted in python tutorials -This is one part of a multi-part tutorial. To see other posts in the same series, please click below:
Part 4 - Make a “flapping” flappy bird
In this part, we will finalize the game in some minor details.
Add base to the game
Inside the images
directory, there’s one image called base.png
, which represents the base of the screen, and should be blitz
beneath the background.png
.
The screen height should be the total height of background
and base
, while the width should stay the same. Since the base is more like a decoration than a real part of the game logic, you don’t have to adjust the drawing of anything to the new screen size.
Something like this will work:
base = pygame.image.load("images/base.png")
base_height = base.get_height()
full_height = height + base_height
size = (width, full_height)
screen = pygame.display.set_mode(size)
while 1:
screen.blit(background, (0, 0))
screen.blit(base, (0, height))
Pipe gap to variate
Have you noticed that since the beginning of the game, we have been using two pipes with fixed top_pipe_height
(100 and 50)? That’s kind of boring, and the player can easily notice that we only have two pipes in our system. What we want to achieve is that the pipe’s top_pipe_height
changes randomly every time it gets reset (i.e. moved back to the beginning position). This can be done quite easily with the random library in python3. You can choose one of several different functions in this library, but as we want to get one random integer in a range of number, random.randint
seems to be the optimal choice.
The largest top_pipe_height
can get is the actual value of pipe_height
, because if top_pipe_height
gets larger than that, we will tend to have a flying pipe due to a gap between the top screen and the toppipe
. Similarly, the height of the botpipe
should not be larger than pipe_height
, which makes the smallest top_pipe_height
can get height - GAP - pipe_height
.
In reality, that range might be still too wide, which makes the game too difficult. You can narrow down the range if needed
While we’re at it, we can also remove the initial longitude, as we don’t need to customize that number now:
class Pipe:
def __init__(self):
self.top_pipe_height = random.randint(height - GAP - pipe_height, pipe_height)
self.longitude = width + pipe_width
self.initial_longitude = self.longitude
self.visible = False
...
def reset(self):
self.top_pipe_height = random.randint(height - GAP - pipe_height, pipe_height)
self.longitude = self.initial_longitude
self.visible = False
Adding sound effects
In the assets
that we downloaded in part 1, there’s a folder called audio
, let’s move it out to the project’s main directory.
mv flappy-bird-assets-master/audio audio
A quick search for sound
in the Pygame doc will show you this page. Basically the way pygame
manages the sounds is similar to the way it manages images: first you load the audio file to a Sound
object, then you can run sound.play()
, which will play the sound. Since all flappy bird’s sounds are short, we probably don’t need stop()
or any other functions.
First, let’s load up all the sounds. There are two versions of the same sound for each sound: wav
and ogg
. If you don’t know what OGG is, it is a free and good audio format file, but it might not run well on some system (it runs in mine, though). Though wav
is a little bit heavier, it’s supposed to run in a wider range of system, so we will choose wav
here (our audio files are not big anyway).
# Load sounds
point_sound = pygame.mixer.Sound("audio/point.wav")
swoosh_sound = pygame.mixer.Sound("audio/swoosh.wav")
hit_sound = pygame.mixer.Sound("audio/hit.wav")
die_sound = pygame.mixer.Sound("audio/die.wav")
Now our job is to add something_sound.play()
into the places where it’s appropriate: swoosh_sound
when the bird jumps, point_sound
when it scores, hit_sound
when it touches the pipe and die_sound
when it dies.
Since hit_sound
and die_sound
go together, you may want to wait for hit_sound
to finish before start playing die_sound
. For that you can use:
pygame.time.delay(int(hit_sound.get_length()*1000))
By default, the .get_length()
returns length in float seconds, bu pygame.time.delay
requires an integer number of millisecond, hence the conversion.
Improve the coding style
Since the beginning of this series, we have been coding everything in the same file, and rarely change the file structure unless we feel a need. What we ended up with is a mixture of classes and global variables, and we don’t really have a good and consistent coding style. That’s normally how it goes when we learn a new thing, but in production, that is not good, and you will get more and more troubles if you want to fix errors/ add functionalities to the project later on.
For that reason, it’s important that after making the project functioning, every once and a while, we refactor our code in a understandable manner, so that other people, as well as our future selves, can understand. We will need a much larger series if we want to include this refactoring phase here, but if you could, please check out the final code, which I will link at the end of the article.
Some other ideas for improving the game
I hope that by now, you have gotten some ideas about how to make a game with pygame
. We only have time to cover the basics, but here are some ideas that you may want to look at if you want to upgrade this game:
- Have some logic to store the scores (to a file, a database, etc.)
- Change game’s screen size (you can stack two background side-by-side and increase the number of pipes in pipe_system)
- Have more levels (increasing the difficulty by reducing GAP, reducing DISTANCE and widening the random range of
top_pipe_length
) - Have pipes moving in up-down direction.
And of course, the sky is the limit for your creativity. This game is now yours, please feel free to do anything you want with it.
You can find the final version of the game in my Github repo
To see other posts in the same series, please click below: