Python Forum
[PyGame] Flappy Bird Improvements
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Flappy Bird Improvements
#1
I'm new here and into Python, so bare with me. I coded Flappy Bird following along with Clear Code's YouTube channel (https://www.youtube.com/watch?v=UZg49z76cLw) and everything works fine. I've scaled it down from the original since it was took long height wise for my screen. Anyways, I'm wanting to do 3 things to improvement, and believe me, if I knew how to or I found my answer on Google, I wouldn't be here as a last resort. Anything I've tried or could think about didn't work. Anyways, here's my list of improvements I'm wanting to do. Coding is provided.

1. Make a "start screen" so the Flappy Bird logo shows up and keeps the game "paused" instead of starting instantly upon opening. In the code/assets folder I have, this is named message.png. Right now, the way the video has it coded, I replaced with gameover.png so when you hit the pipe, it brings up game over. Basically I'm wanting Start Menu (message.png), Game Itself, then Game Over(gameover.png).
2. Maybe this can go along with the start screen with some buttons, but I'd like to incorporate a way to switch between day/night mode as the assets I have comes with a background-day.png and a background-night.png along with blue, red, and yellowbird.. as of now, I'm using background-day with a blue bird.
3. Lastly, and I thought the easiest and could've sworn I could do it, but keeping track of the high score. As of now, the game will keep track of high score, but once you close out of the game, and reopen it, the high score resets. I'd like for it to keep the high score at all times.

Attached Files

.py   flappybird.py (Size: 5.79 KB / Downloads: 142)
Reply
#2
Creating different screen is not really that I hard.
To get a different screen you can look at this post.
I set up a start screen and an end game screen.

https://python-forum.io/thread-43442-pos...#pid182319

The files I used are here

For keeping high scores you will need to write to a text file or database.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#3
_____
Reply
#4
(Oct-31-2024, 12:11 AM)menator01 Wrote: Creating different screen is not really that I hard.
To get a different screen you can look at this post.
I set up a start screen and an end game screen.

https://python-forum.io/thread-43442-pos...#pid182319

The files I used are here

For keeping high scores you will need to write to a text file or database.

Haven't figured out the start screen yet, but did manage to get the game to WRITE to a txt document, but can't get it to read the score back and while it saves, once i close out of the game, it overwrites it in the txt document. Say i got a high score of 8 in the first game, it saves 8, but then I close out and started a second game and get a high score of 4, then the 4 overwrites the 8. I used a snippet of the code you provided but edited it in a way I thought would work. Feel like I'm getting close, but got stuck in a lump somewhere. New code attached to the reply if you want to look over the changes.

Attached Files

.py   flappybird.py (Size: 5.97 KB / Downloads: 59)
Reply
#5
Here is a basic example of pygame pages

Here is a read on reading and writing and appending to files.
https://www.geeksforgeeks.org/reading-wr...es-python/

Although it can be done with functions, I recommend learning to use classes. It will make the job a lot easier in my opinion.

import pygame
from collections import deque

pygame.init()

clock = pygame.time.Clock()
fps = 60

screen_size = (1280, 740)
screen = pygame.display.set_mode(screen_size)

# Colors
black = (0,0,0)
white = (255,255,255)

# Setup pages
mypages = deque(['start', 'play', 'end'])
gamestate = mypages[0]


class Pages:
    ''' Class for creating pages '''
    def _start(self):
        ''' Function for start page '''
        font = pygame.font.SysFont(None, 40)
        text = 'This is the start page'
        surface = font.render(text, True, (255,255,255))
        screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2,  20))

        font = pygame.font.SysFont(None, 30)
        text = 'Press n key for next page and b key for previous page'
        surface = font.render(text, True, (125,125,125))
        screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2,  60))

        pygame.display.update()

    def _play(self):
        ''' Function for play page '''
        font = pygame.font.SysFont(None, 40)
        text = 'This is the game play page'
        surface = font.render(text, True, (255,255,255))
        screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2,  20))

        font = pygame.font.SysFont(None, 30)
        text = 'Press n key for next page and b key for previous page'
        surface = font.render(text, True, (125,125,125))
        screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2,  60))

        pygame.display.update()

    def _end(self):
        ''' Function for end game page '''
        font = pygame.font.SysFont(None, 40)
        text = 'This is the game end page'
        surface = font.render(text, True, (255,255,255))
        screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2,  20))

        font = pygame.font.SysFont(None, 30)
        text = 'Press n key for next page and b key for previous page'
        surface = font.render(text, True, (125,125,125))
        screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2,  60))

        pygame.display.update()


# Initialize the Pages class
page = Pages()

running = True

while running:
    screen.fill(black)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # Track keydown events and set gamestate
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_n:
                mypages.rotate(-1)
                gamestate = mypages[0]
            
            if event.key == pygame.K_b:
                mypages.rotate(1)
                gamestate = mypages[0]

    # What page to show
    if gamestate == 'start':
        page._start()

    if gamestate == 'play':
        page._play()

        pygame.display.update()
        clock.tick(fps)

    if gamestate == 'end':
        page._end()
    

    pygame.display.update()
    clock.tick(fps)

pygame.quit()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#6
Here is an example of your code to change background from day to night. d key changes to day (default) and n key changes to night. The code does nothing other than that.

import pygame

pygame.init()

clock = pygame.time.Clock()
fps = 120

screen_size = (1280, 740)
screen = pygame.display.set_mode(screen_size)

# Colors
black = (0,0,0)
white = (255,255,255)

def page():
    screen.fill((0,0,0))
    img = pygame.image.load('assets/message.png')
    img = pygame.transform.scale2x(img)
    screen.blit(img, (screen_size[0]/2 - img.get_width()/2, screen_size[1]/2 - img.get_height()/2))

    font = pygame.font.SysFont('04B_19.TTF', 40)
    text = 'Press spacebar to begin - esc to exit'
    surf = font.render(text, True, (150,150,150))
    screen.blit(surf, (screen_size[0]/2-font.size(text)[0]/2, screen_size[1]-font.size(text)[1]*2.5))

    pygame.display.update()


class Background:
    ''' Background class handles the creation of backgrounds 
        be it day or night. Also handles the floor creation '''
    def __init__(self):
        self.floor_x = 0

    def day(self):
        bg = pygame.image.load('assets/background-day.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg

    def night(self):
        bg = pygame.image.load('assets/background-night.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg

    def floor(self):
        _floor = pygame.image.load('assets/base.png').convert()
        _floor = pygame.transform.scale(_floor, (screen_size[0], 80))

        screen.blit(_floor, (self.floor_x,screen_size[1]-80))
        screen.blit(_floor, (self.floor_x + screen_size[0], screen_size[1]-80))


timestate = True

timer = pygame.USEREVENT + 1
pygame.time.set_timer(timer, 10000)

background = Background()
bg = background.day()

ticker = pygame.USEREVENT + 2
pygame.time.set_timer(ticker, 1000)
counter = 10

gamestate = 'startpage'
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # Away to exit the game with esc key
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False

            # n key pressed, change to night background
            if event.key == pygame.K_n:
                bg = background.night()
                timestate = False

            # d key pressed change to day background
            if event.key == pygame.K_d:
                bg = background.day()
                timestate = True

            if event.key == pygame.K_SPACE:
                gamestate = 'play'

            if event.key == pygame.K_q:
                gamestate = 'startpage'

        if event.type == ticker:
            counter -= 1
            if counter <= 0:
                counter = 10

        if event.type == timer:
            timestate = False if timestate else True
            if timestate:
                bg = background.day()
            else:
                bg = background.night()

    # Are we on the start page or play
    if gamestate == 'play':
        screen.fill((0,255,0))
        screen.blit(bg, (0,0))

        daynight = 'night' if timestate else 'day'

        text = 'Press q key to go back to start page. esc to quit.'
        text2 = 'Press n key for night and d key for day'
        text3 = 'The screen will alternate between day and night every 10 seconds'
        text4 = f'Will be {daynight} in {counter:02} seconds.'

        font = pygame.font.SysFont('04B_19.TTF', 40)
        tfont = pygame.font.SysFont('04B_19.TTF', 30)

        color = (180,255,0) if counter <= 5 else (0,0,0)

        surf1 = font.render(text, True, (0,0,0))
        surf2 = font.render(text2, True, (0,0,0))
        surf3 = font.render(text3, True, (0,0,0))
        surf4 = tfont.render(text4, True, color)

        screen.blit(surf1, (screen_size[0]/2-font.size(text)[0]/2, 30))
        screen.blit(surf2, (screen_size[0]/2-font.size(text)[0]/2, 70))
        screen.blit(surf3, (screen_size[0]/2-font.size(text)[0]/2, 110))
        screen.blit(surf4, (screen_size[0]/2-font.size(text)[0]/2, 150))

        # Disply and scroll the floor background image
        background.floor_x -= 1
        if background.floor_x <= -screen_size[0]:
            background.floor_x = 0
        background.floor()

        pygame.display.update()
        clock.tick(fps)

    # gamestate has changed go to start/end page
    if gamestate == 'startpage':
        page()

pygame.quit()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#7
Strike through: mean has been added
What it doesn't have:
  • Does not have the bird
  • scoring
  • sound effect
  • Create a Form class for getting player initials
  • Create Database class for storing and retrieving player scores
  • Edit gameover page to show top 5 scores
What it does have is:
  • Changes from day/night every 30 seconds
  • Has three variations of pipe gap positions - close to top, middle, and bottom
  • Hot keys for going to start page during game play, manually change from night/day
    • q = quit game
    • m = night/day mode
    • esc to exit game
    • spacebar on startpage to start game

import pygame
from random import choice
from pathlib import Path
from os import listdir
import sqlite3 as sq

# Get path to executing script
path = Path(__file__).parent

# some pygame variables
pygame.init()

clock = pygame.time.Clock()
fps = 60

screen_size = (1280, 740)
screen = pygame.display.set_mode(screen_size)

# Colors
black = (0,0,0)
white = (255,255,255)

# Create sprite groups
bird_sprite = pygame.sprite.Group()
pipe_sprite = pygame.sprite.Group()
allsprites = pygame.sprite.Group()

# Define the start and end page
def page():
    screen.fill((0,0,0))
    img = pygame.image.load(f'{path}/assets/message.png')
    img = pygame.transform.scale2x(img)
    screen.blit(img, (screen_size[0]/2 - img.get_width()/2, screen_size[1]/2 - img.get_height()/2))

    font = pygame.font.SysFont('04B_19.TTF', 40)
    text = 'Press spacebar to begin - esc to exit. During game play press q to end game.'
    surf = font.render(text, True, (150,150,150))
    screen.blit(surf, (screen_size[0]/2-font.size(text)[0]/2, screen_size[1]-font.size(text)[1]*2.5))

    pygame.display.update()

def gameover(score):
    screen.fill((0,0,0))
    img = pygame.image.load(f'{path}/assets/gameover.png').convert()
    img = pygame.transform.scale2x(img)
    img.set_colorkey(white)
    screen.blit(img, ((screen_size[0]/2-img.get_width()/2, screen_size[1]*0.2)))

    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 60)
    text = 'Press enter to play again or esc to quit'
    surface = font.render(text, True, (255,180,0))
    screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2, screen_size[1]*0.5))

    text2 = f'Final Score: {score}'
    surface = font.render(text2, True, (255,180,0))
    screen.blit(surface, (screen_size[0]/2 - font.size(text)[0]/2, screen_size[1]*0.38))


    pygame.display.update()


class Form:
    def show(self):
        ''' Method will show a form '''
        pass

    def submit(self):
        ''' Method will submit form data to database '''
        pass

    
class DataBase:
    def __init__(self):
        self.connect = sq.connect('bird_score.db')
        self.cursor = self.connect.cursor()

    def insert(self, args):
        ''' Method will write player initials and score to database '''
        if len(args) == 2:
            query = 'insert into score (name, score) values (?,?)'

            self.cursor.execute(query, (args))
            self.connect.commit()
            return True
        return False
        

    def top5(self):
        ''' Method will get top 5 scores '''
        query = '''select * from score desc limit 5'''
        data = [info for info in self.cursor.execute(query)]
        if len(data) > 0:
            return data
        return False
        
        

    def create(self):
        ''' Method will create a table in the database if not exists '''
        query = '''create table if not exists score (
                    name text,
                    score integer
                )'''

        self.connect.execute(query)
        self.connect.commit()


class Background:
    ''' Background class handles the creation of backgrounds 
        be it day or night. Also handles the floor creation '''
    def __init__(self):
        # set an instance variable for floor x pos
        self.floor_x = 0
        
    def day(self):
        ''' Method changes background to day mode '''
        bg = pygame.image.load(f'{path}/assets/background-day.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg

    def night(self):
        ''' Method changes background to night mode '''
        bg = pygame.image.load(f'{path}/assets/background-night.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg

    def floor(self):
        ''' Method displays the floor image '''
        _floor = pygame.image.load(f'{path}/assets/base.png').convert()
        _floor = pygame.transform.scale(_floor, (screen_size[0], 80))

        screen.blit(_floor, (self.floor_x,screen_size[1]-80))
        screen.blit(_floor, (self.floor_x + screen_size[0], screen_size[1]-80))


class Pipe(pygame.sprite.Sprite):
    ''' Class for creating pipes '''
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(f'{path}/assets/pipe-green.png')
        self.rect = self.image.get_rect()
        self.rect.x = screen_size[0] + self.rect.width
        self.rect.bottom = screen_size[1] - 80

        self.active = True

        # Add pipe object to sprite groups
        pipe_sprite.add(self)
        allsprites.add(pipe_sprite)

    def draw_pipe(self, top, bottom, rotate=False):
        ''' Method for drawing the pipes '''
        if rotate:
            self.image = pygame.transform.scale(self.image, (self.rect.width, top))
            self.image = pygame.transform.flip(self.image, False, True)
            self.rect.x = screen_size[0] + self.rect.width
            self.rect.y = 0
        else:
            self.image = pygame.transform.scale(self.image, (self.rect.width, bottom))
            self.rect = self.image.get_rect()
            self.rect.x = screen_size[0] + self.rect.width
            self.rect.bottom = screen_size[1] - 80

    def update(self):
        ''' Method updates the sprite object '''
        # If sprite goes off screen, kill it
        if self.rect.right <= 0:
            self.kill()

        for sprite in bird_sprite:
            if sprite.rect.left >= self.rect.right and self.active:
                score.score += 1
                self.active = False
                channel2.play(sound.play('point'))
                
        # Scrolls the pipes across the screen
        self.rect.x -= speed


class Bird(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.gravity = 0.25
        
        down = pygame.image.load(f'{path}/assets/bluebird-downflap.png').convert_alpha()
        mid = pygame.image.load(f'{path}/assets/bluebird-midflap.png').convert_alpha()
        up = pygame.image.load(f'{path}/assets/bluebird-upflap.png').convert_alpha()
        
        width = up.get_width() + up.get_width()*0.3
        height = up.get_height() + up.get_height()*0.3

        down = pygame.transform.scale(down, (width, height))
        mid = pygame.transform.scale(mid, (width, height))
        up = pygame.transform.scale(up, (width, height))

        self.frames = [down, mid, up]
        self.index = 0

        self.image = self.frames[self.index]     

        self.rect = self.image.get_rect()
        self.rect.x = screen_size[0]/2 - self.rect.width/2
        self.rect.y = screen_size[1]/2 - self.rect.height/2

        bird_sprite.add(self)
        allsprites.add(bird_sprite)

    def rotate(self, angle):
        img = pygame.transform.rotate(self.frames[self.index], angle)
        rect = img.get_rect(center=self.rect.center)
        return img

    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE]:
            # self.image = pygame.transform.rotate(self.frames[self.index], 45)
            self.image = self.rotate(30)
                        
            self.rect.y -= 3.6
        else:
            # self.image = pygame.transform.rotate(self.frames[self.index], -15)
            self.image = self.rotate(-30)
            
        self.rect.y += 2

        if self.rect.top <= 10:
            self.rect.top = 10
            self.image = self.frames[self.index]


class Score:
    def __init__(self):
        self.score = 0

    def show(self):
        font = pygame.font.SysFont('04B_19.TTF', 35)
        text = f'Score: {self}'
        surface = font.render(text, True, (0,0,0))
        screen.blit(surface, (screen_size[0]*0.01, screen_size[1]*0.02))

    def __repr__(self):
        score = int(self.score/2)
        return str(score)


class Sound:
    def __init__(self):
        self.files = listdir(f'{path}/sound')

    def play(self, sfx):
        for file in self.files:
            if sfx in file:
                return pygame.mixer.Sound(f'{path}/sound/{file}')

    def play_wings(self, fx):
        for file in self.files:
            if fx in file:
                pygame.mixer.music.load(f'{path}/sound/{file}')
                pygame.mixer.music.play(-1)
        
# Create channels
channel1 = pygame.mixer.Channel(1)
channel2 = pygame.mixer.Channel(2)
channel3 = pygame.mixer.Channel(3)
channel4 = pygame.mixer.Channel(4)

sound = Sound()

# Game Timers 
mode = True

# Mode timer changes from day/night every 30 seconds
timer = pygame.USEREVENT + 1
pygame.time.set_timer(timer, 30000)

ticker = pygame.USEREVENT + 2
pygame.time.set_timer(ticker, 1000)
counter = 10

# Timer for creating pipes
create_pipe = pygame.USEREVENT + 3
pygame.time.set_timer(create_pipe, 4500)

# Timer for bird wing flap
birdflap = pygame.USEREVENT + 4
pygame.time.set_timer(birdflap, 100)

# Set default background to day mode
background = Background()
bg = background.day()

# background speed
speed = 3

score = Score()

# Setting for going to the start page
gamestate = 'startpage'
running = True
active = True

while running:
######## Event Section ##########################
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        if event.type == birdflap:
            bird.index += 1
            if bird.index >= len(bird.frames):
                bird.index = 0
            bird.image = bird.frames[bird.index]
            
        # Event for creating pipes
        if event.type == create_pipe:
            top = choice([130,270,410])
            bottom = 0

            # We want the correct size for the bottom pipe to match top
            if top == 130:
                bottom = 410
            elif top == 270:
                bottom = 270
            else:
                bottom = 130

            # Create the pipes
            pipe = Pipe()
            pipe.draw_pipe(top, bottom)
            pipe = Pipe()
            pipe.draw_pipe(top, bottom, True)

        # Away to exit the game with esc key
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False

            if event.key == pygame.K_SPACE:
                if active:
                    channel3.play(sound.play('swooshing'))
                    channel4.play(sound.play('wing'), loops = -1)
                
            if event.key == pygame.K_RETURN or event.key == pygame.K_KP_ENTER:
                gamestate = 'play'
                score.score = 0

            if event.key == pygame.K_q:
                gamestate = 'startpage'

            # Check if we are in day or night mode. Change accordingly
            if event.key == pygame.K_m:
                if mode:
                    bg = background.night()
                    mode = False
                elif not mode:
                    bg = background.day()
                    mode = True

        if event.type == timer:
            mode = False if mode else True
            if mode:
                bg = background.day()
            else:
                bg = background.night()

################ gamestate section ##################
    # Are we on the start page or play
    if gamestate == 'play':
        screen.fill((0,255,0))
        screen.blit(bg, (0,0))
        score.show()
        active = True

        # Disply and scroll the floor background image
        background.floor_x -= speed
        if background.floor_x <= -screen_size[0]:
            background.floor_x = 0
        background.floor()

        # If bird crashes into ground, go to gameover page
        if bird.rect.bottom >= screen_size[1] - 80:
            bird.kill()
            channel1.play(sound.play('hit'))
            channel1.queue(sound.play('die'))
            gamestate = 'gameover'


        collisions = pygame.sprite.groupcollide(bird_sprite, pipe_sprite, True, True)
        if collisions:
            channel1.play(sound.play('hit'))
            channel1.queue(sound.play('die'))
            gamestate = 'gameover'

        # Update all game sprites
        allsprites.update()
        allsprites.draw(screen)

        pygame.display.update()
        clock.tick(fps)

    # gamestate has changed go to start/end page
    if gamestate == 'startpage':
        # Go to start page and clear all sprite groups
        page()
        pipe_sprite.empty()
        allsprites.empty()
        bird_sprite.empty()
        bird = Bird()
        active = False


    if gamestate == 'gameover':
        # Go to game oover page
        gameover(score)
        pipe_sprite.empty()
        allsprites.empty()
        bird_sprite.empty()
        bird = Bird()
        active = False
        channel4.stop()

pygame.quit()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#8
Ran into a hiccup, When going between pipes, sometimes you will die even if you don't hit the pipe.
I don't get it. I using groupcollide. Any insight would be great.


Figured it out
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#9
(Nov-01-2024, 09:20 PM)menator01 Wrote: Ran into a hiccup, When going between pipes, sometimes you will die even if you don't hit the pipe.
I don't get it. I using groupcollide. Any insight would be great.

I've tinkered around and attempted to incorporate some of your example coding to change background to day/night as well as "cleaning" up my coding by adding them to classes like your coding, and somewhere along the line, I broke the game as it won't start now.. So I believe I'm just going to keep what I have and give up on it and call it good. Not sure why I'm having such hard time incorporating the small things when you even gave me example of the coding. Thanks a lot for the help. Maybe I'll come back to it and try again some other time.
menator01 likes this post
Reply
#10
This is my final version. Uses sqlite3 database for keeping scores and shows top 5 scores on the gameover page.
Used tkinter as the form for getting player initials as it takes a lot of code to create a pygame form.
Anyhow, here it is for anyone wanting to play around with it. Note: Still could not find the reason player will die when getting to close to the pipes. Finally figured it out. Was setting the size for the rotated pipe for top. Guess it was ghosting or something.

Updated final code.?
import pygame
from random import choice
from pathlib import Path
from os import listdir
import sqlite3 as sq
import tkinter as tk
 
# Get path to executing script
path = Path(__file__).parent
 
# some pygame variables
pygame.init()
 
clock = pygame.time.Clock()
fps = 60
 
screen_size = (1280, 740)
screen = pygame.display.set_mode(screen_size)
 
# Colors
black = (0,0,0)
white = (255,255,255)
 
# Create sprite groups
bird_sprite = pygame.sprite.Group()
pipe_sprite = pygame.sprite.Group()
allsprites = pygame.sprite.Group()
 
# Create channels
channel1 = pygame.mixer.Channel(1)
channel2 = pygame.mixer.Channel(2)
channel3 = pygame.mixer.Channel(3)
channel4 = pygame.mixer.Channel(4)

# Load logo image
logo = pygame.image.load(f'{path}/assets/my-python-logo.png').convert()
logo = pygame.transform.scale(logo,(logo.get_width()/2, logo.get_height()/2))
 
# Define the start and end page
def page():
    screen.fill((0,0,0))
    img = pygame.image.load(f'{path}/assets/message.png')
    img = pygame.transform.scale2x(img)
    screen.blit(img, (screen_size[0]/2 - img.get_width()/2, screen_size[1]/2 - img.get_height()/2))
 
    font = pygame.font.SysFont('04B_19.TTF', 40)
    text = 'Press spacebar to begin - esc to exit. During game play press q to end game.'
    surf = font.render(text, True, (150,150,150))
    screen.blit(surf, (screen_size[0]/2-font.size(text)[0]/2, screen_size[1]-font.size(text)[1]*2.5))
 
    screen.blit(logo, (50,20))

    surf = font.render(chr(169), True, (250,130,0))
    screen.blit(surf, (35, 20))

    surf = font.render(chr(8482), True, (250,130,0))
    screen.blit(surf, (160, 25))

    pygame.display.update()
 
 
def gameover(score):
    row = screen_size[1]/2 - screen_size[1]/2*0.2
    screen.fill((0,0,0))

    screen.blit(logo, (50,20))
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 35)

    surf = font.render(chr(169), True, (250,130,0))
    screen.blit(surf, (35, 20))

    surf = font.render(chr(8482), True, (250,130,0))
    screen.blit(surf, (160, 25))

    img = pygame.image.load(f'{path}/assets/gameover.png').convert()
    img = pygame.transform.scale2x(img)
    img.set_colorkey(white)
    screen.blit(img, ((screen_size[0]/2-img.get_width()/2, screen_size[1]*0.2)))
 
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 50)
    text1 = f'Your Final Score: {score}'
    row_align = screen_size[0]/2 - font.size(text1)[0]/2
    surface = font.render(text1, True, (255,180,0))
    screen.blit(surface, (screen_size[0]/2 - font.size(text1)[0]/2, screen_size[1]*0.38-35))
 
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 60)
    text2 = 'Press enter to play again or esc to quit'
    surface = font.render(text2, True, (255,180,0))
    screen.blit(surface, (screen_size[0]/2 - font.size(text2)[0]/2, screen_size[1]*0.85))
 
    font = pygame.font.SysFont(f'{path}/04B_19.TTF', 40)
    font.set_underline(True)
    text3 = 'Top Scores'
    surface = font.render(text3, True, (255,0,0))
    screen.blit(surface, (screen_size[0]/2-font.size(text3)[0]/2, row+10))
 
    entries = db.top5()
    if entries:
        font = pygame.font.SysFont(f'{path}/04B_19.TTF', 40)
        for entry in entries:
            name, score = entry
            row += 40
            surface = font.render(f'{name}: {score}', True, (255,255,255))
            screen.blit(surface, ((screen_size[0]/2-font.size(text3)[0]/2)+30, row+10))
        row = 105
     
    pygame.display.update()
 
        
class Database:
    def __init__(self):
        self.connect = sq.connect(f'{path}/bird_score.db')
        self.cursor = self.connect.cursor()
 
    def insert(self, args):
        ''' Method will write player initials and score to database '''
        if len(args) == 2:
            query = 'insert into flappy (name, score) values (?,?)'
 
            self.cursor.execute(query, args)
            self.connect.commit()
            return True
        return False
         
 
    def top5(self):
        ''' Method will get top 5 scores '''
        query = '''select * from flappy order by score desc limit 5'''
        data = [info for info in self.cursor.execute(query)]
        if len(data) > 0:
            return data
        return False
         
         
 
    def create(self):
        ''' Method will create a table in the database if not exists '''
        query = '''create table if not exists flappy (
                    name text,
                    score integer
                )'''
 
        self.connect.execute(query)
        self.connect.commit()
 
 
class Background:
    ''' Background class handles the creation of backgrounds 
        be it day or night. Also handles the floor creation '''
    def __init__(self):
        # set an instance variable for floor x pos
        self.floor_x = 0
         
    def day(self):
        ''' Method changes background to day mode '''
        bg = pygame.image.load(f'{path}/assets/background-day.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg
 
    def night(self):
        ''' Method changes background to night mode '''
        bg = pygame.image.load(f'{path}/assets/background-night.png').convert()
        bg = pygame.transform.scale(bg, screen_size)
        return bg
 
    def floor(self):
        ''' Method displays the floor image '''
        _floor = pygame.image.load(f'{path}/assets/base.png').convert()
        _floor = pygame.transform.scale(_floor, (screen_size[0], 80))
 
        screen.blit(_floor, (self.floor_x,screen_size[1]-80))
        screen.blit(_floor, (self.floor_x + screen_size[0], screen_size[1]-80))
 
 
class Pipe(pygame.sprite.Sprite):
    ''' Class for creating pipes '''
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(f'{path}/assets/pipe-green.png')
        self.rect = self.image.get_rect()
        self.rect.x = screen_size[0] + self.rect.width
        self.rect.bottom = screen_size[1] - 80
 
        self.active = True
 
        # Add pipe object to sprite groups
        pipe_sprite.add(self)
        allsprites.add(pipe_sprite)
 
    def draw_pipe(self, top, bottom, rotate=False):
        ''' Method for drawing the pipes '''
        if rotate:
            self.rect.height = top
            self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
            self.image = pygame.transform.flip(self.image, False, True)
            self.rect.x = screen_size[0] + self.rect.width
            self.rect.top = 0

        else:
            self.rect.height = bottom
            self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
            self.rect = self.image.get_rect()
            self.rect.x = screen_size[0] + self.rect.width
            self.rect.bottom = screen_size[1] - 80
 
    def update(self):
        ''' Method updates the sprite object '''
        # If sprite goes off screen, kill it
        if self.rect.right <= 0:
            self.kill()
 
        for sprite in bird_sprite:
            if sprite.rect.left >= self.rect.right and self.active:
                # Setting score to 0.5 because ther are two pipes.
                # We only want one point
                score.score += 0.5
                self.active = False
                channel2.play(sound.play('point'))
                 
        # Scrolls the pipes across the screen
        self.rect.x -= speed
 
 
class Bird(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.gravity = 0.25
         
        # Load the images
        down = pygame.image.load(f'{path}/assets/bluebird-downflap.png').convert_alpha()
        mid = pygame.image.load(f'{path}/assets/bluebird-midflap.png').convert_alpha()
        up = pygame.image.load(f'{path}/assets/bluebird-upflap.png').convert_alpha()
 
        # Scale images
        down = pygame.transform.scale2x(down)
        mid = pygame.transform.scale2x(mid)
        up = pygame.transform.scale2x(up)
 
        # Load the frame list with images and set index
        self.frames = [down, mid, up]
        self.index = 0
 
        # Load first image
        self.image = self.frames[self.index]     
 
        # Get image rect and set start position to middle of screen
        self.rect = self.image.get_rect()
        self.rect.x = screen_size[0]/2 - self.rect.width/2
        self.rect.y = screen_size[1]/2 - self.rect.height/2
 
        # Add sprite to sprite groups
        # bird_sprite for collison detection and allsprites for animation
        bird_sprite.add(self)
        allsprites.add(bird_sprite)
 
    def rotate(self, angle):
        ''' Method for pointer bird up or down '''
        img = pygame.transform.rotate(self.frames[self.index], angle)
        rect = img.get_rect(center=self.rect.center)
        return img
 
    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE]:
            self.image = self.rotate(30)

            # Speed for when pointing up            
            self.rect.y -= 3.6
        else:
            self.image = self.rotate(-30)
        
        # Default speed when going down
        self.rect.y += 2
 
        # Keep bird from flying off top of screen
        if self.rect.top <= 10:
            self.rect.top = 10
            self.image = self.frames[self.index]
 
 
class Score:
    def __init__(self):
        self.score = 0
 
    def show(self):
        font = pygame.font.SysFont('04B_19.TTF', 35)
        text = f'Score: {self}'
        surface = font.render(text, True, (0,0,0))
        screen.blit(surface, (screen_size[0]*0.01, screen_size[1]*0.02))
 
    def __repr__(self):
        score = int(self.score)
        return str(score)
 
 
class Sound:
    def __init__(self):
        self.files = listdir(f'{path}/sound')
 
    def play(self, sfx):
        for file in self.files:
            if sfx in file:
                return pygame.mixer.Sound(f'{path}/sound/{file}')
 
    def play_wings(self, fx):
        for file in self.files:
            if fx in file:
                pygame.mixer.music.load(f'{path}/sound/{file}')
                pygame.mixer.music.play(-1)
         
 
class Window:
    def __init__(self, parent):
        self.parent = parent
        font = (None, 14, 'normal')
        label = tk.Label(parent, text='Enter Initials:', font=font)
        label.grid(column=0, row=0, padx=(4,2), pady=(4,2))
 
        self.myvar = tk.StringVar()
        self.entry = tk.Entry(parent, textvariable=self.myvar, font=font)
        self.entry.grid(column=1, row=0, padx=(2,4), pady=2)
        self.entry.focus()
 
        self.btn = tk.Button(parent, text='Submit', command=self.submit)
        self.btn.grid(column=0, columnspan=2, sticky='e', padx=4, pady=(2,4))
 
        self.entry.bind('<Return>', self.submit)
        self.entry.bind('<KP_Enter>', self.submit)
 
        self.myvar.trace('w', self.validate)
 
    def validate(self, var=None, index=None, mode=None):
        if len(self.myvar.get().strip()) == 0:
            return False
        elif len(self.myvar.get().strip()) > 3:
            self.entry.delete(3, 'end')
            return False
        else:
            return True
     
    def submit(self, event=None):
        if self.validate():
            db.insert((self.myvar.get().strip(), score.score))
            self.parent.destroy()
        else:
            return False
 
# Initialize some classes
sound = Sound()
score = Score()
 
# Inialize Database class and create the table
# if it doesnt exist
db = Database()
db.create()
# Game Timers 
mode = True
 
# Mode timer changes from day/night every 30 seconds
timer = pygame.USEREVENT + 1
pygame.time.set_timer(timer, 30000)
 
# Timer for creating pipes
create_pipe = pygame.USEREVENT + 2
pygame.time.set_timer(create_pipe, 4500)
 
# Timer for bird wing flap
birdflap = pygame.USEREVENT + 3
pygame.time.set_timer(birdflap, 100)
 
# Set default background to day mode
background = Background()
bg = background.day()
 
# background speed
speed = 3

# Setting for going to the start page
gamestate = 'startpage'
running = True
active = True

while running:
######## Event Section ##########################
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
 
        if event.type == birdflap:
            bird.index += 1
            if bird.index >= len(bird.frames):
                bird.index = 0
            bird.image = bird.frames[bird.index]
             
        # Event for creating pipes
        if event.type == create_pipe:
            top = choice([135,275,415])
            bottom = 0
 
            # We want the correct size for the bottom pipe to match top
            if top == 135:
                bottom = 415
            elif top == 275:
                bottom = 275
            else:
                bottom = 135
 
            # Create the pipes
            pipe = Pipe()
            pipe.draw_pipe(top, bottom)
            pipe = Pipe()
            pipe.draw_pipe(top, bottom, True)
 
        # Away to exit the game with esc key
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False
 
            if event.key == pygame.K_SPACE:
                if active:
                    channel3.play(sound.play('swooshing'))
                    channel4.play(sound.play('wing'), loops = -1)
                 
            if event.key == pygame.K_RETURN or event.key == pygame.K_KP_ENTER:
                gamestate = 'play'
                score.score = 0
 
            if event.key == pygame.K_q:
                gamestate = 'startpage'
 
 
            # Check if we are in day or night mode. Change accordingly
            if event.key == pygame.K_m:
                if mode:
                    bg = background.night()
                    mode = False
                elif not mode:
                    bg = background.day()
                    mode = True
 
        if event.type == timer:
            mode = False if mode else True
            if mode:
                bg = background.day()
            else:
                bg = background.night()
 
################ gamestate section ##################
    # Are we on the start page or play
    if gamestate == 'play':
        screen.fill((0,255,0))
        screen.blit(bg, (0,0))
        score.show()
        active = True
 
        # Disply and scroll the floor background image
        background.floor_x -= speed
        if background.floor_x <= -screen_size[0]:
            background.floor_x = 0
        background.floor()
 
        # If bird crashes into ground, go to gameover page
        if bird.rect.bottom >= screen_size[1] - 80:
            bird.kill()
            channel1.play(sound.play('hit'))
            channel1.queue(sound.play('die'))
            gamestate = 'form'
 
 
        collisions = pygame.sprite.groupcollide(bird_sprite, pipe_sprite, True, False)
        if collisions:
            channel1.play(sound.play('hit'))
            channel1.queue(sound.play('die'))
            gamestate = 'form'
 
        # Update all game sprites
        allsprites.update()
        allsprites.draw(screen)
 
        pygame.display.update()
        clock.tick(fps)
 
    # gamestate has changed go to start/end page
    if gamestate == 'startpage':
        # Go to start page and clear all sprite groups
        page()
        pipe_sprite.empty()
        allsprites.empty()
        bird_sprite.empty()
        bird = Bird()
        active = False
 
    if gamestate == 'gameover':
        # Go to game over page
        gameover(int(score.score))
        pipe_sprite.empty()
        allsprites.empty()
        bird_sprite.empty()
        bird = Bird()
        active = False
        
 
    if gamestate == 'form': 
        if score.score > 0:
            channel4.stop()
            root = tk.Tk()
            Window(root)
            root.mainloop()
        active = False
        channel4.stop()
        gamestate = 'gameover'
         
pygame.quit()
screenshot of gameover page

Attached Files

Thumbnail(s)
   
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Game "Fluppy bird" nastia_nadashkevich 1 1,443 Apr-19-2024, 08:45 PM
Last Post: deanhystad
  [PyGame] bird jump in flappy bird games syafiq14 1 3,504 Dec-14-2020, 02:31 PM
Last Post: MK_CodingSpace
  Suggestions/Improvements (pygame) SheeppOSU 3 3,846 Apr-16-2019, 07:37 PM
Last Post: SheeppOSU

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020