Movatterモバイル変換


[0]ホーム

URL:


How to Make a Flappy Bird Game in Python

Learn how to build a flappy bird game from scratch using the Pygame library in Python.
  · 16 min read · Updated aug 2023 ·Game Development

Get a head start on your coding projects with ourPython Code Generator. Perfect for those times when you need a quick solution. Don't wait, try it today!

Flappy Bird is a classic and addictive game that has captured the hearts of millions with its simple yet challenging gameplay. In this tutorial, we will guide you through the process of building your very own Flappy Bird game from scratch, using thePygame module, a popular Python library forgame development.

With just a basic understanding of Python and a dash of creativity, you can harness Pygame's capabilities to create a fun and engaging gaming experience that can be customized to your liking.

Game Setup

Let's start by making surePygame is installed in your computer, head to your terminal and installpygame module usingpip:

$ pip install pygame

After that, create a directory for the game and create the following.py files inside it:settings.py,main.py,world.py,game.py,pipe.py,bird.py. Create also another folder inside the game directory and name itassets, which we'll use to store game media files. Here is the file structure of our code:

Now we can start coding.Let's define our game variables and functions insettings.py:

# settings.pyfrom os import walkimport pygameWIDTH, HEIGHT = 600, 650pipe_pair_sizes = [    (1, 7),    (2, 6),    (3, 5),    (4, 4),    (5, 3),    (6, 2),    (7, 1)]pipe_size = HEIGHT // 10pipe_gap = (pipe_size * 2) + (pipe_size // 2)ground_space = 50def import_sprite(path):    surface_list = []    for _, __, img_file in walk(path):        for image in img_file:            full_path = f"{path}/{image}"            img_surface = pygame.image.load(full_path).convert_alpha()            surface_list.append(img_surface)    return surface_list

Next, let's create the main class of our game inmain.py which also contains the game's main loop:

# main.pyimport pygame, sysfrom settings import WIDTH, HEIGHT, ground_spacefrom world import Worldpygame.init()screen = pygame.display.set_mode((WIDTH, HEIGHT + ground_space))pygame.display.set_caption("Flappy Bird")class Main:    def __init__(self, screen):        self.screen = screen        self.bg_img = pygame.image.load('assets/terrain/bg.png')        self.bg_img = pygame.transform.scale(self.bg_img, (WIDTH, HEIGHT))        self.ground_img = pygame.image.load('assets/terrain/ground.png')        self.ground_scroll = 0        self.scroll_speed = -6        self.FPS = pygame.time.Clock()        self.stop_ground_scroll = False    def main(self):        world = World(screen)        while True:            self.stop_ground_scroll = world.game_over            self.screen.blit(self.bg_img, (0, 0))            for event in pygame.event.get():                if event.type == pygame.QUIT:                    pygame.quit()                    sys.exit()                elif event.type == pygame.KEYDOWN:                    if not world.playing and not world.game_over:                        world.playing = True                    if event.key == pygame.K_SPACE:                        world.update("jump")                    if event.key == pygame.K_r:                        world.update("restart")            world.update()            self.screen.blit(self.ground_img, (self.ground_scroll, HEIGHT))            if not self.stop_ground_scroll:                self.ground_scroll += self.scroll_speed                if abs(self.ground_scroll) > 35:                    self.ground_scroll = 0            pygame.display.update()            self.FPS.tick(60)if __name__ == "__main__":    play = Main(screen)    play.main()

Themain() function serves as the entry point of the game and is responsible for running the game loop. The game loop is designed to keep the game running continuously until it is exited or closed by the player.

At the beginning of the loop, aWorld instance calledworld is created, which represents the game world and holds information about the game's state. TheWorld class handles the gameplay mechanics, such as the bird's movement and collision detection with obstacles. Inside the loop, the first step is to set theself.stop_ground_scroll to the current value ofworld.game_over. This controls whether the scrolling of the ground should stop when the game is over, providing a smooth transition between game sessions. Next, the background image (self.bg_img) is drawn onto the game's screen at the coordinates(0, 0) usingself.screen.blit() method. This ensures that the screen is refreshed with the updated background image before other elements are drawn.

The game then checks for events usingpygame.event.get() to detect any user input, such as clicking the close button to quit the game. If the user presses the space key, theworld.update("jump") method is called to handle the bird's jump action. If the'r' key is pressed, theworld.update("restart") method is triggered, allowing the player to restart the game after a game-over.

After handling events, theworld.update() method updates the game state and applies physics to the game objects. This includes moving the bird and the obstacles, checking for collisions, and updating the score.

Following theworld update, the ground image (self.ground_img) is drawn onto the screen at theself.ground_scroll position, creating the illusion of the ground scrolling. Ifself.stop_ground_scroll isFalse, the ground will scroll horizontally based on theself.scroll_speed value. The scrolling creates the impression of continuous movement in the game world.

Finally,pygame.display.update() is called to update the game display with the latest changes made in this iteration of the game loop. Theself.FPS.tick(60) line ensures that the game runs at a maximum frame rate of 60 frames per second, controlling the speed of the game loop and making it consistent across different devices.

[here]

Creating the Game World

Now that we have our game loop and main class, let's move on to creating our class for the game world:

# world.pyimport pygamefrom pipe import Pipefrom bird import Birdfrom game import GameIndicatorfrom settings import WIDTH, HEIGHT, pipe_size, pipe_gap, pipe_pair_sizesimport randomclass World:    def __init__(self, screen):        self.screen = screen        self.world_shift = 0        self.current_x = 0        self.gravity = 0.5        self.current_pipe = None        self.pipes = pygame.sprite.Group()        self.player = pygame.sprite.GroupSingle()        self._generate_world()        self.playing = False        self.game_over = False        self.passed = True        self.game = GameIndicator(screen)    # adds pipe once the last pipe added reached the desired pipe horizontal spaces    def _add_pipe(self):        pipe_pair_size = random.choice(pipe_pair_sizes)        top_pipe_height, bottom_pipe_height = pipe_pair_size[0] * pipe_size, pipe_pair_size[1] * pipe_size        pipe_top = Pipe((WIDTH, 0 - (bottom_pipe_height + pipe_gap)), pipe_size, HEIGHT, True)        pipe_bottom = Pipe((WIDTH, top_pipe_height + pipe_gap), pipe_size, HEIGHT, False)        self.pipes.add(pipe_top)        self.pipes.add(pipe_bottom)        self.current_pipe = pipe_top    # creates the player and the obstacle    def _generate_world(self):        self._add_pipe()        bird = Bird((WIDTH//2 - pipe_size, HEIGHT//2 - pipe_size), 30)        self.player.add(bird)

Once ourmain() function initializes a world using theWorld class, the_generate_world() function will be called which generates the game world by adding a pipe (using self._add_pipe()) and the bird player character to the game. Withrandom.choice(pipe_pair_sizes) in_add_pipe() function, we can have a pair of pipe obstacles to the game with random sizes.

Next, let's add someworld components for handling the game physics:

# world.py    # for moving background/obstacle    def _scroll_x(self):        if self.playing:            self.world_shift = -6        else:            self.world_shift = 0    # add gravity to bird for falling    def _apply_gravity(self, player):        if self.playing or self.game_over:            player.direction.y += self.gravity            player.rect.y += player.direction.y    # handles scoring and collision    def _handle_collisions(self):        bird = self.player.sprite        # for collision checking        if pygame.sprite.groupcollide(self.player, self.pipes, False, False) or bird.rect.bottom >= HEIGHT or bird.rect.top <= 0:            self.playing = False            self.game_over = True        else:            # if player pass through the pipe gaps            bird = self.player.sprite            if bird.rect.x >= self.current_pipe.rect.centerx:                bird.score += 1                self.passed = True

The_scroll_x() function is responsible for moving the game background and obstacles horizontally, creating the illusion of a scrolling effect. When the game is in the "playing" state, it sets theself.world_shift value to-6, causing the game world to move to the left. When the game is not in the "playing" state (e.g., game over or not started yet),self.world_shift is set to 0, halting the scrolling effect.

The_apply_gravity() function adds gravity to the game's bird character, causing it to fall gradually. When the game is in either the "playing" or "game over" state, the bird'sdirection.y (vertical movement) is increased by the gravity value, simulating a downward force. The bird's position is then updated accordingly by adding thedirection.y value to its current vertical position.

The_handle_collisions() function manages the game's collision detection, scoring, and game-over conditions. It checks for collisions between the player (bird) and the pipes. If a collision occurs, or if the bird's rect (collision bounding box) goes above the screen or below the game's height, the game enters the "game over" state, settingself.playing toFalse andself.game_over toTrue. Otherwise, if the player successfully passes through the gap between the pipes, their score is incremented, andself.passed is set toTrue. This function effectively determines the core gameplay mechanics of scoring points and detecting game-ending collisions.

Let's create one more function in theworld class which combines all of the world's characteristics:

# world.py    # updates the bird's overall state    def update(self, player_event = None):        # new pipe adder        if self.current_pipe.rect.centerx  <= (WIDTH // 2) - pipe_size:            self._add_pipe()        # updates, draws pipes        self.pipes.update(self.world_shift)        self.pipes.draw(self.screen)        # applying game physics        self._apply_gravity(self.player.sprite)        self._scroll_x()        self._handle_collisions()        # configuring player actions        if player_event == "jump" and not self.game_over:            player_event = True        elif player_event == "restart":            self.game_over = False            self.pipes.empty()            self.player.empty()            self.player.score = 0            self._generate_world()        else:            player_event = False        if not self.playing:            self.game.instructions()        # updates, draws pipes        self.player.update(player_event)        self.player.draw(self.screen)        self.game.show_score(self.player.sprite.score)

Theupdate() function is responsible for managing various aspects of the game, including scrolling the pipes, applying gravity to the bird, handling collisions, responding to player input, updating the player's score, and controlling the game's flow.

To start, the function keeps track of the current pipe's position, and when it reaches a certain point on the screen (the middle minus half the pipe size), it adds a new pipe to keep the gameplay challenging. The function then updates the positions of all the pipes and applies the scrolling effect to give the impression of the bird continuously flying through a dynamic environment. Next, gravity is applied to the bird's vertical movement, making it fall gradually. The function ensures that gravity is only applied when the game is in the "playing" or "game over" state.

To handle collisions and game-over conditions, the function checks for collisions between the bird and the pipes or the screen boundaries. If a collision occurs, the game enters the "game over" state, halting the gameplay and displaying the final score. The function also responds to player input events. If the player requests a jump (by pressing a key or tapping the screen), it triggers the bird's upward movement. Additionally, the function allows the player to restart the game after a game-over, resetting all game elements to their initial state.

When the game is not in the "playing" state, the function displays the game instructions, guiding the player on how to play. Finally, the function updates and displays the player's score on the screen during gameplay, incrementing the score whenever the bird successfully passes through the gaps between pipes.

For the handling game instructions and player score, let's create another class and name itGameIndicator:

# game.pyimport pygamefrom settings import WIDTH, HEIGHTpygame.font.init()class GameIndicator:    def __init__(self, screen):        self.screen = screen        self.font = pygame.font.SysFont('Bauhaus 93', 60)        self.inst_font = pygame.font.SysFont('Bauhaus 93', 30)        self.color = pygame.Color("white")        self.inst_color = pygame.Color("black")    def show_score(self, int_score):        bird_score = str(int_score)        score = self.font.render(bird_score, True, self.color)        self.screen.blit(score, (WIDTH // 2, 50))    def instructions(self):        inst_text1 = "Press SPACE button to Jump,"        inst_text2 = "Press \"R\" Button to Restart Game."        ins1 = self.inst_font.render(inst_text1, True, self.inst_color)        ins2 = self.inst_font.render(inst_text2, True, self.inst_color)        self.screen.blit(ins1, (95, 400))        self.screen.blit(ins2, (70, 450))

In theshow_score() function, the player's score is converted to a string, and then rendered using the specified font and color. The resulting score text is then placed on the screen at the horizontal center and 50 pixels from the top. As for theinstructions() function, it renders the game instructions using two strings for the text. These instructions are displayed on the screen with a different font and color, appearing at specific positions (95, 400) and (70, 450) respectively, providing guidance to the player on how to jump and restart the game.

Adding Game Components

And for the game components, we have the bird which represents the player character, and the pipes as the game obstacles. Let's create theBird class first inbird.py.

# bird.pyimport pygamefrom settings import import_spriteclass Bird(pygame.sprite.Sprite):    def __init__(self, pos, size):        super().__init__()        # bird basic info        self.frame_index = 0        self.animation_delay = 3        self.jump_move = -9        # bird animation        self.bird_img = import_sprite("assets/bird")        self.image = self.bird_img[self.frame_index]        self.image = pygame.transform.scale(self.image, (size, size))        self.rect = self.image.get_rect(topleft = pos)        self.mask = pygame.mask.from_surface(self.image)        # bird status        self.direction = pygame.math.Vector2(0, 0)        self.score = 0    # for bird's flying animation    def _animate(self):        sprites = self.bird_img        sprite_index = (self.frame_index // self.animation_delay) % len(sprites)        self.image = sprites[sprite_index]        self.frame_index += 1        self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))        self.mask = pygame.mask.from_surface(self.image)        if self.frame_index // self.animation_delay > len(sprites):            self.frame_index = 0    # to make the bird fly higher    def _jump(self):        self.direction.y = self.jump_move    # updates the bird's overall state    def update(self, is_jump):        if is_jump:            self._jump()        self._animate()

TheBird class inherits frompygame.sprite.Sprite, making it suitable for use with sprite groups in Pygame. It is initialized with a position pos and a size, along with various attributes related to the bird's animation, movement, and status.

In the__init__() method, the bird's basic information, such as the current frame index for animation, the animation delay, and the jump movement (how high the bird jumps), is set. The bird's animation frames are loaded from the specified image file using theimport_sprite() function from thesettings module. The first frame of the animation is scaled to the desiredsize, and its position is set topos. The bird's mask, used for collision detection, is also initialized based on the image's transparency. The bird'sdirection is represented by a 2D vector (pygame.math.Vector2) initially set to (0, 0), representing no initial movement. Thescore attribute is used to keep track of the player's score during gameplay.

The_animate() method handles the bird's animation. It iterates through the bird's animation frames, updating the displayed image at a specified delay to create a smooth animation. Once the last frame is reached, the animation resets to the first frame, creating a looping effect.

The_jump() method is responsible for making the bird fly higher. It updates the bird'sdirection.y attribute to a negative value (self.jump_move), causing the bird to move upwards when called.

Theupdate() method is the core of the bird's state. It takes a boolean argumentis_jump, indicating whether the bird should perform a jump. Ifis_jump istrue, the bird's_jump() method is called to make it move upwards. The_animate() method is then called to handle the animation of the bird.

Let's now create a class for pipes in our game:

# pipe.pyimport pygameclass Pipe(pygame.sprite.Sprite):    def __init__(self, pos, width, height, flip):        super().__init__()        self.width = width        img_path = 'assets/terrain/pipe.png'        self.image = pygame.image.load(img_path)        self.image = pygame.transform.scale(self.image, (width, height))        if flip:            flipped_image = pygame.transform.flip(self.image, False, True)            self.image = flipped_image        self.rect = self.image.get_rect(topleft = pos)    # update object position due to world scroll    def update(self, x_shift):        self.rect.x += x_shift        # removes the pipe in the game screen once it is not shown in the screen anymore        if self.rect.right < (-self.width):            self.kill()

[here]

ThePipe class manages the pipes' appearance, positioning, and updates during the gameplay.

In the__init__() method, thePipe class is initialized with thepos (position),width,height, andflip parameters. The image file path for the pipe is set, and the image is loaded and scaled to the specifiedwidth andheight. Ifflip isTrue, the image is flipped vertically to create the illusion of the pipes coming from the top.

Theupdate() method is responsible for updating the position of the pipe due to the world scroll. It takes thex_shift parameter, which represents the horizontal shift caused by the world scrolling. This method increments theself.rect.x (horizontal position) of the pipe byx_shift, effectively moving the pipe to the left along with the world scrolling.

Additionally, theupdate() method checks if the pipe has moved entirely off the left side of the game screen (whenself.rect.right is less than-self.width or the width of the pipe). If this happens, it calls thekill() built-in method to remove the pipe from the game, freeing up resources and maintaining a clean game environment.

And now, we are done coding the game. To test if everything works, run themain.py file. Here are some of the game snapshots:

Check the demo video:

In this tutorial, we've explored the process of creating the iconic Flappy Bird game using Python and the Pygame library. With a straightforward and concise approach, we have built a game that challenges players to navigate a bird through a series of pipes with just a few lines of code. By understanding the game loop, physics, collisions, and animation, we've constructed a dynamic and engaging gaming experience.

Through theBird class, we managed the bird's animation, movement, and status, while thePipe class controlled the appearance, positioning, and updates of the pipes. TheWorld class played a central role in orchestrating the game's core mechanics, handling events, updating the game state, and rendering graphics.

By following this guide, you now possess the knowledge and tools to venture further into the world of Python game development. With your newfound skills, you can explore additional features, such as sound effects, multiple levels, and high-score tracking, to enhance the Flappy Bird game or even create your own original games.

Checkthe complete code here.

Related game tutorials:

Happy coding ♥

Let ourCode Converter simplify your multi-language projects. It's like having a coding translator at your fingertips. Don't miss out!

View Full Code Understand My Code
Sharing is caring!



Read Also


How to Create a Platformer Game in Python
How to Make a Maze Game in Python
How to Make a Snake Game in Python

Comment panel

    Got a coding query or need some guidance before you comment? Check out thisPython Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!





    Ethical Hacking with Python EBook - Topic - Top


    Join 50,000+ Python Programmers & Enthusiasts like you!



    Tags


    New Tutorials

    Popular Tutorials


    Ethical Hacking with Python EBook - Topic - Bottom

    CodingFleet - Topic - Bottom






    Claim your Free Chapter!

    Download a Completely Free Practical Python PDF Processing Chapter.

    See how the book can help you build handy PDF Processing tools!



    [8]ページ先頭

    ©2009-2026 Movatter.jp