Posts: 4
Threads: 1
Joined: Oct 2024
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.
Posts: 1,145
Threads: 114
Joined: Sep 2019
Oct-31-2024, 12:11 AM
(This post was last modified: Oct-31-2024, 12:11 AM by menator01.)
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.
Posts: 4
Threads: 1
Joined: Oct 2024
Oct-31-2024, 02:20 AM
(This post was last modified: Oct-31-2024, 02:28 AM by KickinItWith_JDub.)
Posts: 4
Threads: 1
Joined: Oct 2024
(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.
Posts: 1,145
Threads: 114
Joined: Sep 2019
Oct-31-2024, 09:20 AM
(This post was last modified: Oct-31-2024, 09:21 AM by menator01.)
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()
Posts: 1,145
Threads: 114
Joined: Sep 2019
Oct-31-2024, 09:40 PM
(This post was last modified: Oct-31-2024, 09:40 PM by menator01.)
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()
Posts: 1,145
Threads: 114
Joined: Sep 2019
Nov-01-2024, 05:43 AM
(This post was last modified: Nov-03-2024, 03:03 AM by menator01.)
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()
Posts: 1,145
Threads: 114
Joined: Sep 2019
Nov-01-2024, 09:20 PM
(This post was last modified: Nov-03-2024, 03:04 AM by menator01.)
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
Posts: 4
Threads: 1
Joined: Oct 2024
(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
Posts: 1,145
Threads: 114
Joined: Sep 2019
Nov-03-2024, 03:54 AM
(This post was last modified: Nov-03-2024, 03:54 AM by menator01.)
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)
|