Part 3

Starting point

At this point your code should look like this:

TITLE = 'Flappy Bird'
WIDTH = 400
HEIGHT = 708

def update():
    barry_the_bird.y += barry_the_bird.speed
    top_pipe.x += scroll_speed
    bottom_pipe.x += scroll_speed
    if top_pipe.right < 0:
        top_pipe.left = WIDTH
        bottom_pipe.left = WIDTH

def draw():
    screen.blit('background', (0, 0))
    barry_the_bird.draw()
    top_pipe.draw()
    bottom_pipe.draw()

def on_mouse_down():
    print ('The mouse was clicked')
    barry_the_bird.y -= 50

barry_the_bird = Actor('bird1', (75, 350))
barry_the_bird.speed = 1

gap = 140
top_pipe = Actor('top', (300, 0))
bottom_pipe = Actor('bottom', (300, top_pipe.height + gap))

scroll_speed = -1

Flying back to the start

What happens when Barry falls off the bottom of the screen? Right now you probably lose him forever. Let’s try to do something better.

  • Add this new function after the on_mouse_down function:
def reset():
    print ("Back to the start...")
    barry_the_bird.speed = 1
    barry_the_bird.center = (75, 350)
    top_pipe.center = (300, 0)
    bottom_pipe.center = (300, top_pipe.height + gap)

Each line in this function is assigning a value. First it sets Barry’s speed back to what it started as, then puts his center at an x,y position. It also moves the pipes back to their starting points. If you try the game now you’ll see that nothing has changed. Remember that a function won’t do anything until you call it. Let’s call it from the update function if Barry goes off the screen.

  • Add this to the end of the update function:
if barry_the_bird.y > HEIGHT:
    reset()
  • Make sure you indent it so that it’s part of the update function, but not part of the previous if statement. Indenting means adding the right number of spaces to the beginning of the line.
  • Check that everthing resets if Barry falls off the bottom of the screen.

Ask a mentor for help if you have trouble getting this to work.

We also need to reset the game if Barry goes off the top of the screen.

  • Change your code to make this happen.

Hint: you will need the or keyword.

Time to Flap!

At the moment, Barry the bird seems to teleport upwards when you click the screen (play the game now if you don’t remember). We’d really like him to move a bit more smoothly. The reason he seems to move instantaneosly is this line:

barry_the_bird.y -= 50

This makes him move a whole 50 pixels all at once. Not very smooth! When a real bird flaps its wings it changes the bird’s speed, not its position. Change in position is just a side effect of having speed.

  • Change your on_mouse_down function to this:
def on_mouse_down():
    barry_the_bird.speed = -6.5

Did that work? Try it and you’ll see that when you flap now he’ll just go up and hit the top of the screen.

We need some gravity to pull him back down again after each flap!

  • Create a variable called gravity at the end of your file:
gravity = 0.3

And we’ll use this to change the bird’s speed every frame.

  • Add this to the beginning of the update function:
barry_the_bird.speed += gravity

Try changing the value of gravity to see what effect it has

Now this bird is more flappy! Controlling him now takes a bit more skill. You can try to fly through the gaps, but we still haven’t done anything to stop you flying straight through the pipes. We’ll fix that soon. But first…

Being a lazy coder

You might have noticed that there are some lines of code that we’ve had to type in twice in different places. For example, barry_the_bird.speed = 1. We do it once in the game setup code, and then again in the reset() function, which is called when Barry dies and the game starts again. Well maybe it would make sense to just use the reset() function at the beginning of the game as well! Then we’d only need the code in one place.

  • Add a call to reset() at the very end of the file.
  • Now we can delete the barry_the_bird.speed = 1 call that happens in the game setup code.

Can you figure out a way to specify the position of everthing in just one part of the code?

Hint: You should be able to remove 3 position values. One of them looks like: (75, 350)

Check the everything still works the same as before.

Giving Barry a head start

Let’s give the player a bit more time to flap before they fall off the screen. We can move the start point to just 50 pixels away from the top of the screen. Find this line in the reset function:

barry_the_bird.center = (75, 350)
  • Change the 350 to something much smaller. Maybe 50? Try it and find a value that seems right to you.

Isn’t it great this number is only in one place in the code? If we hadn’t done the last section (Being a lazy coder) we would have to change two different numbers! It would have been very easy to forget about one of them.

Collisions

In PyGameZero there’s nothing to stop you drawing multiple sprites (images) on top of each other. So if we want certain behaviour when things collide we need to take care of it ourselves. Add this code to the end of the update function:

if (barry_the_bird.colliderect(top_pipe)):
    hit_pipe()

The colliderect function checks if two objects are touching. Because this is inside the update function it will get checked every frame. This won’t work yet because we haven’t created the hit_pipe function. Let’s create it after the reset function…

def hit_pipe():
    print ("Hit pipe!")
    barry_the_bird.image = "birddead"

Try this out. Now Barry should become a ghost when you hit the top pipe, but it looks like there are still three problems:

  1. Barry stays as a ghost even when the game resets.
  2. Barry can still fly through the bottom pipe!
  3. You can still flap and fly along even as a ghost.
  • Try to fix problems 1 & 2 now. We’ll look at 3 together.

Hint for number 1 : Barry started with the “bird1” image, but it changes to “birddead” when he hits a pipe. Find the right place to change it back.

Hint for number 2 : Remember the or keyword.

Now might be a good time to try changing the size of gap to tune the difficulty of the game. You might want to make it very big while testing, so you can focus on testing and not on flying!

Now we’d like to stop Barry from flapping while he’s a ghost. The code which makes him fly needs a way of knowing if he’s still alive. We could use the barry_the_bird.image variable, because that changes when he dies. But it’s better to add a new variable to make our code cleaner and less likely to break if we make changes later.

  • Add this line to the reset function (Being a lazy coder pays off again!):
barry_the_bird.alive = True

We’re creating the new variable alive and setting it to true. Now we need to make sure barry only flaps when he’s alive.

  • Add this line to the beginning of the on_mouse_down function:
if (barry_the_bird.alive):

Don’t forget to change the indentation (number of spaces at the beginning) of the line that changes the speed, so that it’s part of the if statement. We only want to change the speed (in other words flap) if the bird is alive!

The next thing to do is to change barry to not be alive when he hits a pipe.

  • Add a line to change barry’s alive variable to False when he hits a pipe.

Well done. That’s the end of part 3. In the next part we’ll look at a few finishing touches such as adding a flapping animation, randomizing the pipe positions, and keeping score.

Go to Part 4.

Extra Challenges

  • Turn physics upside down! Make gravity pull Barry upwards, and make flapping push him downwards
  • Add a cheat key that makes the player invincible

Hint: Try adding this function and see what happens when you press a key:

def on_key_down(key):
    print(key)
  • Add a secret tiny flap that the player can do using the right mouse button

Hint: you will need to add a parameter to your on_mouse_down function, so it becomes on_mouse_down(button). Try use the print function like you did in the last challenge to see what values button has.