├── .gitignore ├── assets ├── audio │ ├── die.ogg │ ├── die.wav │ ├── hit.ogg │ ├── hit.wav │ ├── point.ogg │ ├── point.wav │ ├── wing.ogg │ ├── wing.wav │ ├── swoosh.ogg │ └── swoosh.wav └── sprites │ ├── 0.png │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── base.png │ ├── play.png │ ├── gameover.png │ ├── message.png │ ├── pipe-red.png │ ├── pipe-green.png │ ├── background-day.png │ ├── background-night.png │ ├── bluebird-midflap.png │ ├── bluebird-upflap.png │ ├── redbird-downflap.png │ ├── redbird-midflap.png │ ├── redbird-upflap.png │ ├── bluebird-downflap.png │ ├── yellowbird-midflap.png │ ├── yellowbird-upflap.png │ └── yellowbird-downflap.png ├── game_state.py ├── README.md ├── LICENSE ├── bird.py ├── pipe.py ├── game_variables.py └── flappy.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | __pycache__ 3 | .DS_Store -------------------------------------------------------------------------------- /assets/audio/die.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/die.ogg -------------------------------------------------------------------------------- /assets/audio/die.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/die.wav -------------------------------------------------------------------------------- /assets/audio/hit.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/hit.ogg -------------------------------------------------------------------------------- /assets/audio/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/hit.wav -------------------------------------------------------------------------------- /assets/sprites/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/0.png -------------------------------------------------------------------------------- /assets/sprites/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/1.png -------------------------------------------------------------------------------- /assets/sprites/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/2.png -------------------------------------------------------------------------------- /assets/sprites/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/3.png -------------------------------------------------------------------------------- /assets/sprites/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/4.png -------------------------------------------------------------------------------- /assets/sprites/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/5.png -------------------------------------------------------------------------------- /assets/sprites/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/6.png -------------------------------------------------------------------------------- /assets/sprites/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/7.png -------------------------------------------------------------------------------- /assets/sprites/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/8.png -------------------------------------------------------------------------------- /assets/sprites/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/9.png -------------------------------------------------------------------------------- /assets/audio/point.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/point.ogg -------------------------------------------------------------------------------- /assets/audio/point.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/point.wav -------------------------------------------------------------------------------- /assets/audio/wing.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/wing.ogg -------------------------------------------------------------------------------- /assets/audio/wing.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/wing.wav -------------------------------------------------------------------------------- /assets/audio/swoosh.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/swoosh.ogg -------------------------------------------------------------------------------- /assets/audio/swoosh.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/audio/swoosh.wav -------------------------------------------------------------------------------- /assets/sprites/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/base.png -------------------------------------------------------------------------------- /assets/sprites/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/play.png -------------------------------------------------------------------------------- /assets/sprites/gameover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/gameover.png -------------------------------------------------------------------------------- /assets/sprites/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/message.png -------------------------------------------------------------------------------- /assets/sprites/pipe-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/pipe-red.png -------------------------------------------------------------------------------- /assets/sprites/pipe-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/pipe-green.png -------------------------------------------------------------------------------- /assets/sprites/background-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/background-day.png -------------------------------------------------------------------------------- /assets/sprites/background-night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/background-night.png -------------------------------------------------------------------------------- /assets/sprites/bluebird-midflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/bluebird-midflap.png -------------------------------------------------------------------------------- /assets/sprites/bluebird-upflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/bluebird-upflap.png -------------------------------------------------------------------------------- /assets/sprites/redbird-downflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/redbird-downflap.png -------------------------------------------------------------------------------- /assets/sprites/redbird-midflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/redbird-midflap.png -------------------------------------------------------------------------------- /assets/sprites/redbird-upflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/redbird-upflap.png -------------------------------------------------------------------------------- /assets/sprites/bluebird-downflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/bluebird-downflap.png -------------------------------------------------------------------------------- /assets/sprites/yellowbird-midflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/yellowbird-midflap.png -------------------------------------------------------------------------------- /assets/sprites/yellowbird-upflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/yellowbird-upflap.png -------------------------------------------------------------------------------- /assets/sprites/yellowbird-downflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iJohnMaged/FlapPy-Bird/HEAD/assets/sprites/yellowbird-downflap.png -------------------------------------------------------------------------------- /game_state.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains the class representing the game state, A simple Enum. 3 | """ 4 | from enum import Enum 5 | 6 | 7 | class State(Enum): 8 | MAIN_MENU = 0 9 | PLAYING = 1 10 | GAME_OVER = 2 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlapPy-Bird 2 | 3 | Python 3 implemention of FlappyBird game using [Arcade Library](http://arcade.academy/) 4 | 5 | ## Installation and usage 6 | 7 | Make sure to install Arcade library from [here](http://arcade.academy/installation.html) 8 | and then you can run the game using the command 9 | 10 | `python flappy.py` 11 | 12 | ## Todo 13 | 14 | * [ ] Train my [Neural Network](https://github.com/iJohnMaged/Simple-NeuralNetwork-Py) 15 | implemention to play the game. 16 | 17 | ### Screenshots 18 | 19 | 20 |
21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 John Maged 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bird.py: -------------------------------------------------------------------------------- 1 | import math 2 | import random 3 | # BAD BAD BAD 4 | from game_variables import * 5 | 6 | 7 | class Bird(arcade.AnimatedTimeSprite): 8 | 9 | def __init__(self, center_x, center_y, death_height): 10 | super().__init__(center_x=center_x, center_y=center_y) 11 | self.score = 0 12 | self.textures = [] 13 | rnd = random.SystemRandom() 14 | # Randomly choose a bird color 15 | color = rnd.choice(list(BIRDS)) 16 | for i in range(4): 17 | self.textures.append(arcade.load_texture(BIRDS[color][i % 3])) 18 | 19 | self.cur_texture_index = 0 20 | self.vel = 0 21 | self.dy = 0 22 | self.death_height = death_height 23 | self.dead = False 24 | 25 | def set_velocity(self, velocity): 26 | self.vel = velocity 27 | 28 | def update(self, dt=0): 29 | if self.dead: 30 | self.angle = -90 31 | if self.center_y > self.death_height + self.height//2: 32 | self.center_y -= 4 33 | return 34 | 35 | if self.vel > 0: 36 | # self.center_y += (1 - math.cos((JUMP_DY - self.vel) * math.pi)) * JUMP_STEP 37 | self.center_y += DY 38 | # self.center_y += self.vel 39 | # self.vel = 0 40 | self.vel -= DY 41 | if self.angle < 30: 42 | self.angle = min(self.angle + ANGLEP, 30) 43 | else: 44 | if self.angle > -90: 45 | self.angle = max(self.angle + ANGLEM, -90) 46 | self.center_y -= GRAVITY 47 | 48 | def flap(self): 49 | self.vel = JUMP_DY 50 | 51 | def die(self): 52 | self.dead = True 53 | arcade.play_sound(SOUNDS['die']) 54 | -------------------------------------------------------------------------------- /pipe.py: -------------------------------------------------------------------------------- 1 | import random 2 | # BAD PRACTICE IN 3.. 2.. 1.. GO! 3 | from game_variables import * 4 | 5 | pipe = random.choice(PIPES) 6 | 7 | 8 | class Pipe(arcade.Sprite): 9 | 10 | def __init__(self, image, scale=1): 11 | """ 12 | Initializer for the pipe object, it's not really correct to call this Pipe since this class is responsible of 13 | creating two pipes as an obstacle for the bird. 14 | """ 15 | super().__init__(image, scale) 16 | # the amount of pixels the pipe move each frame. 17 | self.horizontal_speed = -1.5 18 | # Just a boolean to check if the bird passed this pipe successfully. 19 | self.scored = False 20 | 21 | @classmethod 22 | def random_pipe_obstacle(cls, sprites, height): 23 | """ 24 | A class method that creates two pipes each with minimum height of MIN_HEIGHT and minimum MIN_GAP distance 25 | between the two pipes. Each obstacle created from this method will be placed exactly 1 pixel out of the screen. 26 | Sprites: Dictionary holding textures to both base and background (to be used as a reference to where to draw 27 | the pipe, could be improved and optimized?) 28 | """ 29 | bottom_pipe = cls(pipe) 30 | bottom_pipe.top = random.randrange(sprites['base'].height + MIN_HEIGHT, height - GAP_SIZE - MIN_HEIGHT) 31 | bottom_pipe.left = sprites['background'].width 32 | 33 | top_pipe = cls(pipe) 34 | top_pipe.angle = 180 35 | top_pipe.left = sprites['background'].width 36 | top_pipe.bottom = bottom_pipe.top + GAP_SIZE 37 | # top_pipe.bottom = random.randrange(bottom_pipe.top + MIN_GAP, height - MIN_HEIGHT) 38 | 39 | return bottom_pipe, top_pipe 40 | 41 | def update(self): 42 | # Move each frame in the negative x direction. 43 | self.center_x += self.horizontal_speed -------------------------------------------------------------------------------- /game_variables.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains very useful variables for the game. 3 | """ 4 | 5 | import os 6 | import arcade 7 | 8 | # List of different pipe images (Green / Red) (Choose one) 9 | PIPES = ["assets" + os.sep + "sprites" + os.sep + "pipe-green.png", "assets" + os.sep + "sprites" + os.sep + "pipe-red.png"] 10 | # Image of the base floor 11 | BASE = "assets" + os.sep + "sprites" + os.sep + "base.png" 12 | # List of different background images (Day / Night) (Choose one) 13 | PLAY_BUTTON = "assets" + os.sep + "sprites" + os.sep + "play.png" 14 | BACKGROUNDS = ["assets" + os.sep + "sprites" + os.sep + "background-day.png", "assets" + os.sep + "sprites" + os.sep + "background-night.png"] 15 | # Dict holding the animation images for different birds colors (Choose one) 16 | BIRDS = {'yellow': ["assets" + os.sep + "sprites" + os.sep + "yellowbird-downflap.png", "assets" + os.sep + "sprites" + os.sep + "yellowbird-midflap.png", 17 | "assets" + os.sep + "sprites" + os.sep + "yellowbird-upflap.png"], 18 | 'red': ["assets" + os.sep + "sprites" + os.sep + "redbird-downflap.png", "assets" + os.sep + "sprites" + os.sep + "redbird-midflap.png", 19 | "assets" + os.sep + "sprites" + os.sep + "redbird-upflap.png"], 20 | 'blue': ["assets" + os.sep + "sprites" + os.sep + "bluebird-downflap.png", "assets" + os.sep + "sprites" + os.sep + "bluebird-midflap.png", 21 | "assets" + os.sep + "sprites" + os.sep + "bluebird-upflap.png"]} 22 | # Start screen (Tap tap!) 23 | GET_READY_MESSAGE = "assets" + os.sep + "sprites" + os.sep + "message.png" 24 | # Game over logo 25 | GAME_OVER = "assets" + os.sep + "sprites" + os.sep + "gameover.png" 26 | # dict mapping sound name to arcade sound object 27 | SOUNDS = {'wing': arcade.load_sound("assets" + os.sep + "audio" + os.sep + "wing.wav"), 28 | 'die': arcade.load_sound("assets" + os.sep + "audio" + os.sep + "die.wav"), 29 | 'hit': arcade.load_sound("assets" + os.sep + "audio" + os.sep + "hit.wav"), 30 | 'point': arcade.load_sound("assets" + os.sep + "audio" + os.sep + "point.wav"), 31 | 'swoosh': arcade.load_sound("assets" + os.sep + "audio" + os.sep + "swoosh.wav")} 32 | # TODO: Better variables names :) 33 | 34 | # Minimum height for a pipe 35 | MIN_HEIGHT = 50 36 | # Minimum gap between two pipes (The gap that a bird can go through) 37 | # MIN_GAP = 100 38 | 39 | GAP_SIZE = 120 40 | 41 | # How many pixels per jump 42 | JUMP_DY = 60 43 | # How many pixels per frame 44 | JUMP_STEP = 4 45 | DY = 2 46 | # Gravity pixels 47 | GRAVITY = 2 48 | 49 | ANGLEP = 15 50 | ANGLEM = -1.5 51 | 52 | # Images for the score board. 53 | SCORE = { 54 | '0': 'assets' + os.sep + 'sprites' + os.sep + '0.png', 55 | '1': 'assets' + os.sep + 'sprites' + os.sep + '1.png', 56 | '2': 'assets' + os.sep + 'sprites' + os.sep + '2.png', 57 | '3': 'assets' + os.sep + 'sprites' + os.sep + '3.png', 58 | '4': 'assets' + os.sep + 'sprites' + os.sep + '4.png', 59 | '5': 'assets' + os.sep + 'sprites' + os.sep + '5.png', 60 | '6': 'assets' + os.sep + 'sprites' + os.sep + '6.png', 61 | '7': 'assets' + os.sep + 'sprites' + os.sep + '7.png', 62 | '8': 'assets' + os.sep + 'sprites' + os.sep + '8.png', 63 | '9': 'assets' + os.sep + 'sprites' + os.sep + '9.png', 64 | } 65 | -------------------------------------------------------------------------------- /flappy.py: -------------------------------------------------------------------------------- 1 | import arcade 2 | import random 3 | from bird import Bird 4 | from pipe import Pipe 5 | from game_state import State 6 | from game_variables import * 7 | 8 | 9 | class Game(arcade.Window): 10 | 11 | def __init__(self, width, height): 12 | 13 | """ 14 | Initializer for the game window, note that we need to call setup() on the game object. 15 | """ 16 | super().__init__(width, height, title="Flappy Bird!") 17 | 18 | self.sprites = None 19 | self.pipe_sprites = None 20 | self.bird = None 21 | # Background texture 22 | self.background = None 23 | # Base texture 24 | self.base = None 25 | # List of birds, even though we've only one bird, it's better to draw a SpriteList than to draw a Sprite 26 | self.bird_list = None 27 | # Score texture 28 | self.score_board = None 29 | # A boolean to check if the user tapped 30 | self.flapped = False 31 | # initial score 32 | self.score = None 33 | # Initial state of the game 34 | self.state = State.MAIN_MENU 35 | # The texture for the start and game over screens. 36 | self.menus = {'start': arcade.load_texture(GET_READY_MESSAGE), 37 | 'gameover': arcade.load_texture(GAME_OVER), 38 | 'play': arcade.load_texture(PLAY_BUTTON)} 39 | 40 | def setup(self): 41 | self.score = 0 42 | self.score_board = arcade.SpriteList() 43 | self.pipe_sprites = arcade.SpriteList() 44 | self.bird_list = arcade.SpriteList() 45 | # A dict holding sprites of static stuff like background & base 46 | self.background = arcade.load_texture(random.choice(BACKGROUNDS)) 47 | self.base = arcade.load_texture(BASE) 48 | # A dict holding a reference to the textures 49 | self.sprites = dict() 50 | self.sprites['background'] = self.background 51 | self.sprites['base'] = self.base 52 | # The bird object itself. 53 | # The AnimatedTimeSprite makes an animated sprite that animates over time. 54 | self.bird = Bird(50, self.height//2, self.base.height) 55 | self.bird_list.append(self.bird) 56 | 57 | # Create a random pipe (Obstacle) to start with. 58 | start_pipe1 = Pipe.random_pipe_obstacle(self.sprites, self.height) 59 | self.pipe_sprites.append(start_pipe1[0]) 60 | self.pipe_sprites.append(start_pipe1[1]) 61 | 62 | def draw_score_board(self): 63 | """ 64 | Draws the score board 65 | """ 66 | self.score_board.draw() 67 | 68 | def draw_background(self): 69 | """ 70 | Draws the background. 71 | """ 72 | arcade.draw_texture_rectangle(self.width // 2, self.height // 2, self.background.width, self.background.height, 73 | self.background, 0) 74 | 75 | def draw_base(self): 76 | """ 77 | Bet you expected what this does. :) 78 | """ 79 | arcade.draw_texture_rectangle(self.width//2, self.base.height//2, self.base.width, self.base.height, self.base, 0) 80 | 81 | def on_draw(self): 82 | 83 | """ 84 | This is the method called when the drawing time comes. 85 | """ 86 | # Start rendering and draw all the objects 87 | arcade.start_render() 88 | # Calling "draw()" on a SpriteList object will call it on each child in the list. 89 | 90 | # Whatever the state, we need to draw background, then pipes on top, then base, then bird. 91 | self.draw_background() 92 | self.pipe_sprites.draw() 93 | self.draw_base() 94 | self.bird_list.draw() 95 | 96 | if self.state == State.MAIN_MENU: 97 | # Show the main menu 98 | texture = self.menus['start'] 99 | arcade.draw_texture_rectangle(self.width//2, self.height//2 + 50, texture.width, texture.height, texture, 0) 100 | 101 | elif self.state == State.PLAYING: 102 | # Draw the score board when the player start playing. 103 | self.draw_score_board() 104 | 105 | elif self.state == State.GAME_OVER: 106 | # Draw the game over menu if the player lost + draw the score board. 107 | texture = self.menus['gameover'] 108 | arcade.draw_texture_rectangle(self.width//2, self.height//2 + 50, texture.width, texture.height, texture, 0) 109 | texture = self.menus['play'] 110 | arcade.draw_texture_rectangle(self.width//2, self.height//2 - 100, texture.width, texture.height, texture, 0) 111 | self.draw_score_board() 112 | 113 | def on_key_release(self, symbol, modifiers): 114 | 115 | if symbol == arcade.key.SPACE and self.state == State.MAIN_MENU: 116 | # If the game is starting, just change the state and return 117 | self.state = State.PLAYING 118 | return 119 | if symbol == arcade.key.SPACE: 120 | self.flapped = True 121 | 122 | def on_mouse_press(self, x, y, button, modifiers): 123 | 124 | if self.state == State.GAME_OVER: 125 | texture = self.menus['play'] 126 | h = self.height//2 - 100 127 | w = self.width//2 128 | if w - texture.width//2 <= x <= w + texture.width//2: 129 | if h - texture.height//2 <= y <= h + texture.height//2: 130 | self.setup() 131 | self.state = State.MAIN_MENU 132 | 133 | def build_score_board(self): 134 | """ 135 | Builds the score board with images. Basically how this work: 136 | 1. Calculate how many digits in the score. 137 | 2. Calculate width (Number of digits * width of each digit image width) 138 | 3. Calculate the "left" x position that makes all the images centered. 139 | 4. Just append every digit's image in the score board. 140 | :return: 141 | """ 142 | score_length = len(str(self.score)) 143 | score_width = 24 * score_length 144 | left = (self.width - score_width) // 2 145 | self.score_board = arcade.SpriteList() 146 | for s in str(self.score): 147 | self.score_board.append(arcade.Sprite(SCORE[s], 1, center_x=left + 12, center_y=450)) 148 | left += 24 149 | 150 | def on_update(self, delta_time): 151 | 152 | """ 153 | This is the method called each frame to update objects (Like their position, angle, etc..) before drawing them. 154 | """ 155 | # print(delta_time) 156 | # Whatever the state, update the bird animation (as in advance the animation to the next frame) 157 | self.bird_list.update_animation() 158 | 159 | if self.state == State.PLAYING: 160 | self.build_score_board() 161 | 162 | # If the player pressed space, let the bird fly higher 163 | if self.flapped: 164 | arcade.play_sound(SOUNDS['wing']) 165 | self.bird.flap() 166 | self.flapped = False 167 | 168 | # Check if bird is too high 169 | if self.bird.top > self.height: 170 | self.bird.top = self.height 171 | 172 | # Check if bird is too low 173 | if self.bird.bottom <= self.base.height: 174 | if self.bird.change_y < 0: 175 | self.bird.change_y = 0 176 | self.bird.bottom = self.base.height 177 | 178 | new_pipe = None 179 | 180 | # Kill pipes that are no longer shown on the screen as they're useless and live in ram and create a new pipe 181 | # when needed. (If the center_x of the closest pipe to the bird passed the middle of the screen) 182 | for pipe in self.pipe_sprites: 183 | if pipe.right <= 0: 184 | pipe.kill() 185 | elif len(self.pipe_sprites) == 2 and pipe.right <= random.randrange(self.width // 2, self.width // 2 + 15): 186 | new_pipe = Pipe.random_pipe_obstacle(self.sprites, self.height) 187 | 188 | if new_pipe: 189 | self.pipe_sprites.append(new_pipe[0]) 190 | self.pipe_sprites.append(new_pipe[1]) 191 | 192 | # This calls "update()" Method on each object in the SpriteList 193 | self.pipe_sprites.update() 194 | self.bird.update(delta_time) 195 | self.bird_list.update() 196 | 197 | # If the bird passed the center of the pipe safely, count it as a point. 198 | # Hard coding.. :) 199 | if self.bird.center_x >= self.pipe_sprites[0].center_x and not self.pipe_sprites[0].scored: 200 | arcade.play_sound(SOUNDS['point']) 201 | self.score += 1 202 | # Well, since each "obstacle" is a two pipe system, we gotta count them both as scored. 203 | self.pipe_sprites[0].scored = True 204 | self.pipe_sprites[1].scored = True 205 | print(self.score) 206 | 207 | # Check if the bird collided with any of the pipes 208 | hit = arcade.check_for_collision_with_list(self.bird, self.pipe_sprites) 209 | 210 | if any(hit): 211 | arcade.play_sound(SOUNDS['hit']) 212 | self.state = State.GAME_OVER 213 | self.bird.die() 214 | 215 | elif self.state == State.GAME_OVER: 216 | # We need to keep updating the bird in the game over scene so it can still "die" 217 | self.bird.update() 218 | 219 | 220 | def main(): 221 | game = Game(288, 512) 222 | game.setup() 223 | arcade.run() 224 | 225 | 226 | if __name__ == "__main__": 227 | main() 228 | --------------------------------------------------------------------------------