Pygame Flappy bird tutorial nr. 5 – Sounds and speed

Get the full code and assets in the github repository.

You need sounds to make the game more appealing. So, let’s see how to make it with pygame. I am using pygame 2.0 (pip install pygame==2.0.0.dev10)

Adding sounds for fly and hit

Here I initialize the mixer and load two sounds, jump and hit. I also set the volume, ’cause they were too loud.

pygame.mixer.pre_init(44100, -16, 2, 512)
pygame.init()
pygame.mixer.quit()
pygame.mixer.init(44100, -16, 2, 512)
pygame.mixer.set_num_channels(32)
jump = pygame.mixer.Sound("sounds/jump.wav")
jump.set_volume(0.5)
hit = pygame.mixer.Sound("sounds/hit.wav")
hit.set_volume(0.3)

A function for the sounds

This is just to make the code easier to be written and read

def play(snd):
    "Plays one of the sounds in the sounds folder using play('name')"
    pygame.mixer.Sound.play(snd)

Calling the function to play sounds

Here is where I used the hit and jump

This is when the bird hits a pipe, in the Sprite class, in the check_collision method. I checked if gameover == 0 to make it play once.

    def check_collision(self):
        global gameover

        for pipe in pipes:
            if pygame.sprite.collide_mask(pipe, self):
                print("touched")
                if gameover == 0:
                    play(hit)
                gameover = 1

Here jump sound is played when you hit K_UP in the main function

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    play(jump)
                    moveup = 1
                    startcounter = 1

Add rotation when bird hits the pipe

I made this function:

def rotate(file, angle):
    return pygame.transform.rotate(load(file), angle)

And changed the self.imagefall in Sprite with the bird with eyes close used when it hits a pipe into from this

self.imagefall = load("fall")

to this

self.imagefall = rotate("fall", -45)

In the main function, still, I changed this, adding the kill method at the end:

        if gameover:
            # cannot fly
            moveup = 0
            # goes down
            flappy.rect.top += 1
            flappy.cnt = 0
            flappy.image = flappy.imagefall
            if flappy.rect.top > 600:
                flappy.kill()

Adding rotation when you press “up”

I created another list of images, but rotated (I do not want to rotate them when user presses up, but when program starts). I used the function rotate (see above).

        self.framesup = [rotate(f[:-4], 45) for f in glob(file + "*.png")]

Then I added this code to the update method of the Sprite class to make it happen

    def update(self):
        global moveup, gameover
        # when moveup animation's faster
        self.cnt += .1
        if self.cnt > len(self.frames) - 1:
            self.cnt = 0
        if not moveup:
            self.image = self.frames[int(self.cnt)]
        else:
            self.image = self.framesup[int(self.cnt)]
        if not gameover:
            self.score += 1
        self.check_collision()

Now the bird move like this:

Adding the score to the screen

As you can see in the video above we got the score and also an icon with the bird, exact copy of the one that flies.

after pygame.font.init(), I put this code at the beginning of the code, after sound initialisation…

font = pygame.font.SysFont("Arial", 16)


def write(text_to_show, color="Coral"):
    'write("Start")'
    text = font.render(text_to_show, 1, pygame.Color(color))
    return text

Then I blitted the score like this in the main function in the while loop

        screen.blit(flappy.image, (0, 0))
        screen.blit(text, (50, 0))
        screen.blit(write(str(flappy.score), color="White"), (100, 0))
        screen.blit(write(str(flappy.maxscore), color="White"), (150, 0))

the text above is this:

    text = write("Score")

The maxscore

How can we save the maxscore and load it back at the start o a game?

I reused some code I made for the Snake clone:

import os


class Score:
    def __init__(self, file) -> None:
        "Goes to load_maxscore() to see the last maxscore"
        self.file = file
        self.maxscore = self.load_maxscore()

    def file_is_empty(self) -> bool:
        "Returns True if there is nothing in the file -> writes 10"
        with open(self.file, "r") as file_check:
            f = file_check.read()
        if f == "":
            return True
        else:
            return False

    def save_score(self, score):
        "Saves the score if it's greater than the previous maxscore"
        print(score, self.maxscore)
        if int(score) >= int(self.maxscore):
            self.write_maxscore(str(score))

    def write_maxscore(self, score: int):
        "Write in the score.txt file if it does not exists"
        with open(self.file, "w") as file:
            file.write(str(score))
            self.maxscore = score

    def read_maxscore(self):
        "if the file exists and is not empty reads it and returns the score"
        with open(self.file, "r") as file_saved:
            last_maxscore = int(file_saved.read())
            print("Maxscore = " + str(last_maxscore))
            return last_maxscore

    def file_exists(self) -> True:
        "Check if file exists in the folder"
        if self.file in os.listdir():
            return True
        else:
            return False


    def load_maxscore(self) -> int:
        "If there is a file with a maxscore it returns it\
        so that it will be in self.maxscore,\
        otherwise it will create a new file with a maxscore of 10\
        and will return this 10"
        if self.file_exists():
            if not self.file_is_empty():
                # This reads the score and put in Puuzzle.maxscore
                maxscore = int(self.read_maxscore())
                return maxscore
            else:
                self.write_maxscore("1")
                return 3
        else:
            self.write_maxscore("1")
            return 3

I called this score.py and put it into a folder called functions and imported like this in the flappy main file

from functions.score import *

Then I created an istande of the class Score in the main file with the name of the file where I want to store the maxscore.

score = Score("myscore.txt")

Now it load the previous maxscore through this istance.

I put the maxscore into the Sprite function

        self.maxscore = game.maxscore

To save the new maxscore I just use this:

    def check_collision(self):
        global gameover

        for pipe in pipes:
            if pygame.sprite.collide_mask(pipe, self):
                print("touched")
                if gameover == 0:
                    play(hit)
                    if self.score > self.maxscore:
                        game.save_score(self.score)
                gameover = 1

It checks if the score is greater that the old maxscore when it’s gameover.

New movements

Now when you press arrow key right it speeds up

    speedup = 0
    speedcount = 0
    while loop:
        if speedup:
            for pipe in pipes:
                pipe.speed = 10
                moveup = 1
            flappy.image = flappy.imagespeed 
        else:
            for pipe in pipes:
                pipe.speed = 1

A minimalistic splash screen

def menu():
    "This is the menu that waits you to click the s key to start"
    bb = pygame.image.load("bg.png")
    fl = pygame.image.load("bluebird-downflap.png")
    screen.blit(bb, (0, 0))
    screen.blit(fl, (100, 300))
    loop1 = 1
    screen.blit(write("Flappy Pygame"), (10, 0))
    screen.blit(write("Press any Key"), (10, 50))
    screen.blit(write("Press m to come back to this menu"), (10, 80))
    screen.blit(write("Press arrow key up to fly up and arrow key right to fly fast"), (10, 200))
    while loop1:
        for event in pygame.event.get():
            if (event.type == pygame.QUIT):
                loop1 = 0
            if event.type == pygame.KEYDOWN:
                press_escape = event.key == pygame.K_ESCAPE
                if press_escape:
                    loop1 = 0

                start()
        pygame.display.update()

    pygame.quit()


menu()

Conclusions

So, the game should be refinished… a lot, but there is the basic idea about how to make it, now you could add other stuff to make it more fun or make a completely new game starting from this. It has graphic, sounds, collision detection, scrolling, score, maxscore a splash screen, increasing difficulties. In its simplicity there is all that makes it a complete game. Go here to see how you could change the graphic, making it a completely different game.

Video


Subscribe to the newsletter for updates
Tkinter templates
Avatar My youtube channel

Twitter: @pythonprogrammi - python_pygame

Videos

Speech recognition game

Pygame's Platform Game

Other Pygame's posts

 

Published by pythonprogramming

Started with basic on the spectrum, loved javascript in the 90ies and python in the 2000, now I am back with python, still making some javascript stuff when needed.