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
My youtube channel
Twitter: @pythonprogrammi - python_pygame
Videos
Speech recognition gamePygame's Platform Game
Other Pygame's posts