301 lines
9.8 KiB
Python
301 lines
9.8 KiB
Python
import pygame
|
|
from pygame.locals import *
|
|
import random
|
|
|
|
pygame.init()
|
|
|
|
#implementing timer
|
|
clock = pygame.time.Clock()
|
|
fps = 60
|
|
|
|
#setting screen size based on image assets
|
|
screen_width = 864
|
|
screen_height = 786
|
|
|
|
#creating game window
|
|
screen = pygame.display.set_mode((screen_width, screen_height))
|
|
pygame.display.set_caption('Flappy Bird')
|
|
|
|
#font
|
|
font = pygame.font.SysFont('Bauhaus 93', 60)
|
|
|
|
#color
|
|
white = (255, 255, 255)
|
|
|
|
#game variables
|
|
ground_scroll = 0
|
|
scroll_speed = 4 # 4 pixels
|
|
flying = False #required to determine the start of gameplay
|
|
game_over = False
|
|
pipe_gap = 150
|
|
pipe_frequency = 1500 #miliseconds
|
|
last_pipe = pygame.time.get_ticks() - pipe_frequency
|
|
score = 0
|
|
pass_pipe = False
|
|
#my addition
|
|
flappy_collided = False
|
|
|
|
#loading game assets
|
|
bg = pygame.image.load('img/bg.png')
|
|
ground_img = pygame.image.load('img/ground.png')
|
|
button_img = pygame.image.load('img/restart.png')
|
|
die_fx = pygame.mixer.Sound('sound_effects/die.mp3')
|
|
flap_fx = pygame.mixer.Sound('sound_effects/flap.mp3')
|
|
hit_fx = pygame.mixer.Sound('sound_effects/hit.mp3')
|
|
restart_fx = pygame.mixer.Sound('sound_effects/restart.mp3')
|
|
score_fx = pygame.mixer.Sound('sound_effects/score.mp3')
|
|
|
|
|
|
#function to handle the display of score onto the screen
|
|
def draw_text(text, font, text_col, x, y):
|
|
img = font.render(text, True, text_col)
|
|
screen.blit(img, (x, y))
|
|
|
|
def reset_game():
|
|
pipe_group.empty()
|
|
flappy.rect.x = 100
|
|
flappy.rect.y = int(screen_height / 2)
|
|
score = 0 #this is local variable, wouldn't affect the global so we will
|
|
#return the score so it can be used globally after
|
|
return score
|
|
|
|
#using classes to create the bird
|
|
class Bird(pygame.sprite.Sprite):
|
|
def __init__(self, x, y):
|
|
#add a function to inheret update, draw, etc function from sprite classes
|
|
pygame.sprite.Sprite.__init__(self)
|
|
self.images = []
|
|
self.index = 0
|
|
self.counter = 0 #to control animation speed
|
|
for num in range(1, 4):
|
|
img = pygame.image.load(f'img/bird{num}.png')#load images for bird
|
|
self.images.append(img)
|
|
self.image = self.images[self.index]
|
|
self.rect = self.image.get_rect() #create a rectangle bounding box around the image
|
|
self.rectangles = []
|
|
self.rect.center = [x, y] #setting the starting point of the rectangle
|
|
self.vel = 0 #velocity of the bird (up and down motion)
|
|
self.clicked = False #checking when the mouse is released
|
|
|
|
#overwriting the sprite update function
|
|
def update(self):
|
|
|
|
if flying == True: #start movement on first mouse click
|
|
#handle velocity/movemnet/gravity of the bird
|
|
self.vel += 0.5 #increase velocity of bird at every iteration
|
|
if self.vel > 8: #adding gravity
|
|
self.vel = 8
|
|
if self.rect.bottom < 618:
|
|
self.rect.y += int(self.vel) # if the bird is above ground it will increase in height
|
|
#relative to the rate of increase in velocity for each mouse click
|
|
|
|
if game_over == False:
|
|
#jumping
|
|
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False: #returning a array of mouse buttons (selecting 1 for left mouse button)
|
|
self.clicked = True
|
|
self.vel = -8
|
|
flap_fx.play()
|
|
if pygame.mouse.get_pressed()[0] == 0:
|
|
self.clicked = False
|
|
|
|
#handle the animation (cycling of images to immitate bird flap)
|
|
self.counter += 1 #increase image iteration
|
|
flap_cooldown = 5
|
|
|
|
if self.counter > flap_cooldown:
|
|
self.counter = 0 #start loop counter
|
|
self.index += 1 #increment through images
|
|
|
|
if self.index >= len(self.images): #keep index to the max of images in array
|
|
self.index = 0
|
|
self.image = self.images[self.index]
|
|
|
|
#rotate the bird
|
|
self.image = pygame.transform.rotate(self.images[self.index], self.vel * -2)
|
|
|
|
else:
|
|
|
|
#play hit sound on collision
|
|
# if flappy_collided == True:
|
|
# self.rect.right = 0
|
|
|
|
|
|
#hit_fx.play()
|
|
|
|
|
|
#rotate the bird to the dead position
|
|
self.image = pygame.transform.rotate(self.images[self.index], -90)
|
|
|
|
if self.rect.bottom >= 618 and flying == True:
|
|
die_fx.play()
|
|
|
|
|
|
|
|
class Pipe(pygame.sprite.Sprite):
|
|
def __init__(self, x, y, position):
|
|
pygame.sprite.Sprite.__init__(self)
|
|
self.image = pygame.image.load('img/pipe.png')
|
|
self.rect = self.image.get_rect()
|
|
#position 1 means top and -1 means bottom
|
|
if position == 1:
|
|
self.image = pygame.transform.flip(self.image, False, True)
|
|
self.rect.bottomleft = [x, y - int(pipe_gap / 2 )]
|
|
else:
|
|
self.rect.topleft = [x, y + int(pipe_gap / 2 )]
|
|
|
|
def update(self):
|
|
self.rect.x -= scroll_speed #pipes moving at scroll speed
|
|
if self.rect.right < 0: #if right side of rect or x coord is less than zero
|
|
self.kill()
|
|
|
|
class Button():
|
|
def __init__(self, x, y, image):
|
|
self.image = image
|
|
self.rect = self.image.get_rect()
|
|
self.rect.topleft = (x, y)
|
|
|
|
def draw(self):
|
|
|
|
action = False
|
|
|
|
#get mouse position
|
|
pos = pygame.mouse.get_pos()
|
|
|
|
#check if mouse is over the button by checking for collision
|
|
if self.rect.collidepoint(pos):
|
|
if pygame.mouse.get_pressed()[0] == 1: #checking that mouse was clicked
|
|
action = True
|
|
|
|
#draw button
|
|
screen.blit(self.image, (self.rect.x, self.rect.y))
|
|
|
|
return action #returning a confirmation that the mouse was clicked to restart the game
|
|
|
|
#group to keep track of the sprites being added to it
|
|
#its like a python list
|
|
bird_group = pygame.sprite.Group()
|
|
pipe_group = pygame.sprite.Group()
|
|
|
|
flappy = Bird(100, int(screen_height / 2))
|
|
bird_group.add(flappy)
|
|
|
|
#dealing with the removal of the rectangle on the bird
|
|
#to prevent looping of the hit sound
|
|
rect1 = flappy.rect
|
|
rectangles = [rect1]
|
|
removed_rect1 = None
|
|
|
|
#create restart button instance
|
|
button = Button(screen_width // 2 - 50, screen_height // 2 - 100, button_img)
|
|
|
|
#play sound on collision
|
|
if flappy_collided == True:
|
|
hit_fx.play()
|
|
|
|
|
|
#game play loop starts here
|
|
run = True
|
|
|
|
while run:
|
|
|
|
#telling the clock what speed to tick at in order
|
|
#to slow the scroll speed
|
|
clock.tick(fps)
|
|
|
|
#blit function is being called to load the bg image to screen
|
|
screen.blit(bg, (0,-150))
|
|
|
|
#add bird to screen
|
|
bird_group.draw(screen)
|
|
bird_group.update()
|
|
|
|
#add pipe to screen
|
|
pipe_group.draw(screen)
|
|
|
|
#draw ground
|
|
screen.blit(ground_img, (ground_scroll, 618))
|
|
|
|
#checking score by measuring the bird's travel through each pipe
|
|
#by sensing when its bounding rectangle passes the right and left bounding
|
|
#rectangle of the pipes and incrementing the score variable by 1 each time
|
|
|
|
if len(pipe_group) > 0: #ensuring that pipes have been create before starting
|
|
if bird_group.sprites()[0].rect.left > pipe_group.sprites()[0].rect.left\
|
|
and bird_group.sprites()[0].rect.right < pipe_group.sprites()[0].rect.right\
|
|
and pass_pipe == False:
|
|
pass_pipe = True
|
|
if pass_pipe == True:
|
|
if bird_group.sprites()[0].rect.left > pipe_group.sprites()[0].rect.right:
|
|
score += 1
|
|
score_fx.play()
|
|
pass_pipe = False
|
|
|
|
draw_text(str(score), font, white, int(screen_width/2), 20)
|
|
|
|
|
|
#collision - checking
|
|
if pygame.sprite.groupcollide(bird_group, pipe_group, False, False) or flappy.rect.top < 0:
|
|
flappy_collided = True
|
|
|
|
for r1 in rectangles[:]:
|
|
hit_fx.play()
|
|
removed_rect1 = r1 #sending this value to the reset process
|
|
rectangles.remove(rect1)
|
|
break
|
|
|
|
game_over = True
|
|
|
|
|
|
#check if the bird hit the ground
|
|
if flappy.rect.bottom >= 618:
|
|
game_over = True
|
|
flying = False
|
|
|
|
|
|
|
|
#stop scrolling the ground if game is over
|
|
if game_over == False and flying == True:
|
|
|
|
#generate new pipes
|
|
time_now = pygame.time.get_ticks()
|
|
if time_now - last_pipe > pipe_frequency:
|
|
pipe_height = random.randint(-100, 100)
|
|
btm_pipe = Pipe(screen_width, int(screen_height/2) + pipe_height, -1)
|
|
top_pipe = Pipe(screen_width, int(screen_height/2) + pipe_height, 1 )
|
|
pipe_group.add(btm_pipe)
|
|
pipe_group.add(top_pipe)
|
|
last_pipe = time_now #set last pipe to now (being the last time a pipe was created on each iteration)
|
|
|
|
#scoll ground
|
|
ground_scroll -= scroll_speed
|
|
|
|
#repeating the ground image when it scrolls out of view
|
|
if abs(ground_scroll) > 35: #negative value converted to absolute
|
|
ground_scroll = 0
|
|
|
|
pipe_group.update()
|
|
|
|
|
|
|
|
#check for game over and reset
|
|
if game_over == True:
|
|
if button.draw() == True:
|
|
restart_fx.play()
|
|
game_over = False
|
|
rectangles.append(removed_rect1) #replacing the rectangle on the bird
|
|
score = reset_game() #calling the reset function to reset gameplay and return the score value
|
|
#which is being saved to the global score varibale using this same expression
|
|
|
|
#looking for game events to determin gameplay
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
run = False
|
|
if event.type == pygame.MOUSEBUTTONDOWN and flying == False and game_over == False: #determing game should start on first mouse click
|
|
flying = True
|
|
|
|
#update function called to update entire UI is everything in
|
|
#the loop above it
|
|
pygame.display.update()
|
|
|
|
pygame.quit()
|
|
|