├── code ├── main.py ├── settings.py └── sprites.py ├── graphics ├── environment │ ├── background.png │ └── ground.png ├── font │ └── BD_Cartoon_Shout.ttf ├── obstacles │ ├── 0.png │ └── 1.png ├── plane │ ├── red0.png │ ├── red1.png │ └── red2.png └── ui │ └── menu.png └── sounds ├── jump.wav └── music.wav /code/main.py: -------------------------------------------------------------------------------- 1 | import pygame, sys, time 2 | from settings import * 3 | from sprites import BG, Ground, Plane, Obstacle 4 | 5 | class Game: 6 | def __init__(self): 7 | 8 | # setup 9 | pygame.init() 10 | self.display_surface = pygame.display.set_mode((WINDOW_WIDTH,WINDOW_HEIGHT)) 11 | pygame.display.set_caption('Flappy Bird') 12 | self.clock = pygame.time.Clock() 13 | self.active = True 14 | 15 | # sprite groups 16 | self.all_sprites = pygame.sprite.Group() 17 | self.collision_sprites = pygame.sprite.Group() 18 | 19 | # scale factor 20 | bg_height = pygame.image.load('../graphics/environment/background.png').get_height() 21 | self.scale_factor = WINDOW_HEIGHT / bg_height 22 | 23 | # sprite setup 24 | BG(self.all_sprites,self.scale_factor) 25 | Ground([self.all_sprites,self.collision_sprites],self.scale_factor) 26 | self.plane = Plane(self.all_sprites,self.scale_factor / 1.7) 27 | 28 | # timer 29 | self.obstacle_timer = pygame.USEREVENT + 1 30 | pygame.time.set_timer(self.obstacle_timer,1400) 31 | 32 | # text 33 | self.font = pygame.font.Font('../graphics/font/BD_Cartoon_Shout.ttf',30) 34 | self.score = 0 35 | self.start_offset = 0 36 | 37 | # menu 38 | self.menu_surf = pygame.image.load('../graphics/ui/menu.png').convert_alpha() 39 | self.menu_rect = self.menu_surf.get_rect(center = (WINDOW_WIDTH / 2,WINDOW_HEIGHT / 2)) 40 | 41 | # music 42 | self.music = pygame.mixer.Sound('../sounds/music.wav') 43 | self.music.play(loops = -1) 44 | 45 | def collisions(self): 46 | if pygame.sprite.spritecollide(self.plane,self.collision_sprites,False,pygame.sprite.collide_mask)\ 47 | or self.plane.rect.top <= 0: 48 | for sprite in self.collision_sprites.sprites(): 49 | if sprite.sprite_type == 'obstacle': 50 | sprite.kill() 51 | self.active = False 52 | self.plane.kill() 53 | 54 | def display_score(self): 55 | if self.active: 56 | self.score = (pygame.time.get_ticks() - self.start_offset) // 1000 57 | y = WINDOW_HEIGHT / 10 58 | else: 59 | y = WINDOW_HEIGHT / 2 + (self.menu_rect.height / 1.5) 60 | 61 | score_surf = self.font.render(str(self.score),True,'black') 62 | score_rect = score_surf.get_rect(midtop = (WINDOW_WIDTH / 2,y)) 63 | self.display_surface.blit(score_surf,score_rect) 64 | 65 | def run(self): 66 | last_time = time.time() 67 | while True: 68 | 69 | # delta time 70 | dt = time.time() - last_time 71 | last_time = time.time() 72 | 73 | # event loop 74 | for event in pygame.event.get(): 75 | if event.type == pygame.QUIT: 76 | pygame.quit() 77 | sys.exit() 78 | if event.type == pygame.MOUSEBUTTONDOWN: 79 | if self.active: 80 | self.plane.jump() 81 | else: 82 | self.plane = Plane(self.all_sprites,self.scale_factor / 1.7) 83 | self.active = True 84 | self.start_offset = pygame.time.get_ticks() 85 | 86 | if event.type == self.obstacle_timer and self.active: 87 | Obstacle([self.all_sprites,self.collision_sprites],self.scale_factor * 1.1) 88 | 89 | # game logic 90 | self.display_surface.fill('black') 91 | self.all_sprites.update(dt) 92 | self.all_sprites.draw(self.display_surface) 93 | self.display_score() 94 | 95 | if self.active: 96 | self.collisions() 97 | else: 98 | self.display_surface.blit(self.menu_surf,self.menu_rect) 99 | 100 | pygame.display.update() 101 | # self.clock.tick(FRAMERATE) 102 | 103 | if __name__ == '__main__': 104 | game = Game() 105 | game.run() -------------------------------------------------------------------------------- /code/settings.py: -------------------------------------------------------------------------------- 1 | WINDOW_WIDTH = 480 2 | WINDOW_HEIGHT = 800 3 | FRAMERATE = 120 -------------------------------------------------------------------------------- /code/sprites.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from settings import * 3 | from random import choice, randint 4 | 5 | class BG(pygame.sprite.Sprite): 6 | def __init__(self,groups,scale_factor): 7 | super().__init__(groups) 8 | bg_image = pygame.image.load('../graphics/environment/background.png').convert() 9 | 10 | full_height = bg_image.get_height() * scale_factor 11 | full_width = bg_image.get_width() * scale_factor 12 | full_sized_image = pygame.transform.scale(bg_image,(full_width,full_height)) 13 | 14 | self.image = pygame.Surface((full_width * 2,full_height)) 15 | self.image.blit(full_sized_image,(0,0)) 16 | self.image.blit(full_sized_image,(full_width,0)) 17 | 18 | self.rect = self.image.get_rect(topleft = (0,0)) 19 | self.pos = pygame.math.Vector2(self.rect.topleft) 20 | 21 | def update(self,dt): 22 | self.pos.x -= 300 * dt 23 | if self.rect.centerx <= 0: 24 | self.pos.x = 0 25 | self.rect.x = round(self.pos.x) 26 | 27 | class Ground(pygame.sprite.Sprite): 28 | def __init__(self,groups,scale_factor): 29 | super().__init__(groups) 30 | self.sprite_type = 'ground' 31 | 32 | # image 33 | ground_surf = pygame.image.load('../graphics/environment/ground.png').convert_alpha() 34 | self.image = pygame.transform.scale(ground_surf,pygame.math.Vector2(ground_surf.get_size()) * scale_factor) 35 | 36 | # position 37 | self.rect = self.image.get_rect(bottomleft = (0,WINDOW_HEIGHT)) 38 | self.pos = pygame.math.Vector2(self.rect.topleft) 39 | 40 | # mask 41 | self.mask = pygame.mask.from_surface(self.image) 42 | 43 | def update(self,dt): 44 | self.pos.x -= 360 * dt 45 | if self.rect.centerx <= 0: 46 | self.pos.x = 0 47 | 48 | self.rect.x = round(self.pos.x) 49 | 50 | class Plane(pygame.sprite.Sprite): 51 | def __init__(self,groups,scale_factor): 52 | super().__init__(groups) 53 | 54 | # image 55 | self.import_frames(scale_factor) 56 | self.frame_index = 0 57 | self.image = self.frames[self.frame_index] 58 | 59 | # rect 60 | self.rect = self.image.get_rect(midleft = (WINDOW_WIDTH / 20,WINDOW_HEIGHT / 2)) 61 | self.pos = pygame.math.Vector2(self.rect.topleft) 62 | 63 | # movement 64 | self.gravity = 600 65 | self.direction = 0 66 | 67 | # mask 68 | self.mask = pygame.mask.from_surface(self.image) 69 | 70 | # sound 71 | self.jump_sound = pygame.mixer.Sound('../sounds/jump.wav') 72 | self.jump_sound.set_volume(0.3) 73 | 74 | def import_frames(self,scale_factor): 75 | self.frames = [] 76 | for i in range(3): 77 | surf = pygame.image.load(f'../graphics/plane/red{i}.png').convert_alpha() 78 | scaled_surface = pygame.transform.scale(surf,pygame.math.Vector2(surf.get_size())* scale_factor) 79 | self.frames.append(scaled_surface) 80 | 81 | def apply_gravity(self,dt): 82 | self.direction += self.gravity * dt 83 | self.pos.y += self.direction * dt 84 | self.rect.y = round(self.pos.y) 85 | 86 | def jump(self): 87 | self.jump_sound.play() 88 | self.direction = -400 89 | 90 | def animate(self,dt): 91 | self.frame_index += 10 * dt 92 | if self.frame_index >= len(self.frames): 93 | self.frame_index = 0 94 | self.image = self.frames[int(self.frame_index)] 95 | 96 | def rotate(self): 97 | rotated_plane = pygame.transform.rotozoom(self.image,-self.direction * 0.06,1) 98 | self.image = rotated_plane 99 | self.mask = pygame.mask.from_surface(self.image) 100 | 101 | def update(self,dt): 102 | self.apply_gravity(dt) 103 | self.animate(dt) 104 | self.rotate() 105 | 106 | class Obstacle(pygame.sprite.Sprite): 107 | def __init__(self,groups,scale_factor): 108 | super().__init__(groups) 109 | self.sprite_type = 'obstacle' 110 | 111 | orientation = choice(('up','down')) 112 | surf = pygame.image.load(f'../graphics/obstacles/{choice((0,1))}.png').convert_alpha() 113 | self.image = pygame.transform.scale(surf,pygame.math.Vector2(surf.get_size()) * scale_factor) 114 | 115 | x = WINDOW_WIDTH + randint(40,100) 116 | 117 | if orientation == 'up': 118 | y = WINDOW_HEIGHT + randint(10,50) 119 | self.rect = self.image.get_rect(midbottom = (x,y)) 120 | else: 121 | y = randint(-50,-10) 122 | self.image = pygame.transform.flip(self.image,False,True) 123 | self.rect = self.image.get_rect(midtop = (x,y)) 124 | 125 | self.pos = pygame.math.Vector2(self.rect.topleft) 126 | 127 | # mask 128 | self.mask = pygame.mask.from_surface(self.image) 129 | 130 | def update(self,dt): 131 | self.pos.x -= 400 * dt 132 | self.rect.x = round(self.pos.x) 133 | if self.rect.right <= -100: 134 | self.kill() 135 | -------------------------------------------------------------------------------- /graphics/environment/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/environment/background.png -------------------------------------------------------------------------------- /graphics/environment/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/environment/ground.png -------------------------------------------------------------------------------- /graphics/font/BD_Cartoon_Shout.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/font/BD_Cartoon_Shout.ttf -------------------------------------------------------------------------------- /graphics/obstacles/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/obstacles/0.png -------------------------------------------------------------------------------- /graphics/obstacles/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/obstacles/1.png -------------------------------------------------------------------------------- /graphics/plane/red0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/plane/red0.png -------------------------------------------------------------------------------- /graphics/plane/red1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/plane/red1.png -------------------------------------------------------------------------------- /graphics/plane/red2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/plane/red2.png -------------------------------------------------------------------------------- /graphics/ui/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/graphics/ui/menu.png -------------------------------------------------------------------------------- /sounds/jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/sounds/jump.wav -------------------------------------------------------------------------------- /sounds/music.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clear-code-projects/FlappyBird/539680a5fdbac79737fcc8c5b68a9360ce6df114/sounds/music.wav --------------------------------------------------------------------------------