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()