Python Project: Flappy Bird (Pygame)

Build a Flappy Bird–style game using pygame, physics, collision, and game loop.

Spec Sheet — Flappy Bird (Python Pygame Edition)

Project File: flappy_bird.py Platform: Pygame Window Must Have: Gravity, Flap, Pipes, Collisions, Score, Restart

Goal

Build a Flappy Bird–style game where the player controls a bird that “flaps” upward and must fly through gaps between moving pipes.

Requirements

Window / Display

  • Window size: 400 x 600 (or similar)
  • Title: “Flappy Bird”
  • Background color: simple solid color is fine

Bird

  • Bird is a rectangle or circle (no images required)
  • Gravity pulls bird down each frame
  • Press SPACE to flap upward (set upward velocity)

Pipes

  • Pipes spawn on the right and move left
  • Each pipe set has a top and bottom pipe with a gap
  • Gap size is constant (example: 170 px)
  • Pipes recycle after leaving the screen

Collision

  • Bird hits a pipe → Game Over
  • Bird hits the ground or top of screen → Game Over

Scoring

  • Score increases by 1 when the bird passes a pipe set
  • Score is displayed on screen

Game Over / Restart

  • Show “GAME OVER” message
  • Press R to restart (reset bird, pipes, and score)

Controls

  • SPACE = flap
  • R = restart (after game over)
  • ESC or window close = quit
Install pygame: run pip install pygame before running this project.

Pseudocode

START

SET up pygame
CREATE game window and clock

DEFINE constants (gravity, flap strength, pipe speed, gap size)

FUNCTION reset_game():
    set score = 0
    set game_over = false
    create bird rectangle at starting position
    set bird velocity = 0
    create list of pipes (several pipe sets spaced out)

FUNCTION create_pipe(x_position):
    randomize top pipe height
    create top pipe rect
    create bottom pipe rect (top height + gap)
    mark pipe as not yet scored
    return pipe set

CALL reset_game()

WHILE program running:
    handle events:
        if quit -> exit
        if SPACE and not game_over -> flap (set velocity upward)
        if R and game_over -> reset_game()
        if ESC -> exit

    IF not game_over:
        apply gravity to bird velocity
        move bird vertically

        move pipes left
        if pipe leaves screen -> recycle it to right with new random gap

        check collisions:
            if bird hits pipe OR bird hits ground/top -> game_over

        update score:
            if bird passes pipe center and not scored -> score++

    draw background
    draw pipes
    draw bird
    draw score
    if game_over -> draw "GAME OVER" and restart instructions

    update display and tick clock (FPS)

END

Rubric (100 Points)

Category Excellent (Full Credit) Points
Program Runs / Setup Runs with pygame, clean window loop 10
Bird Physics (Gravity + Flap) Gravity works; flap gives upward velocity 15
Pipes Spawn & Move Pipes move left; gap exists; multiple pipe sets 15
Pipe Recycling Pipes reset after leaving screen 10
Collision Detection Pipe + ground/top collisions end game 15
Scoring Score increments when passing pipes; shown on screen 15
Game Over Screen Shows message clearly 5
Restart Feature Press R resets game correctly 10
Code Quality Good organization, names, comments 5
Total 100

Actual Python Code — flappy_bird.py

"""
File: flappy_bird.py
Flappy Bird (Simple Pygame Version)

Controls:
- SPACE: flap
- R: restart after game over
- ESC: quit
"""

import pygame
import random
import sys


# ----------------------------
# CONFIG
# ----------------------------
WIDTH, HEIGHT = 400, 600
FPS = 60

GRAVITY = 0.45
FLAP_STRENGTH = -8.5

PIPE_WIDTH = 70
PIPE_GAP = 170
PIPE_SPEED = 3.2
PIPE_SPACING = 220  # distance between pipe sets

GROUND_HEIGHT = 60


class PipeSet:
    """
    Stores a top pipe rect, bottom pipe rect, and whether it has been scored.
    """
    def __init__(self, x):
        self.x = x
        self.scored = False
        self.randomize()

    def randomize(self):
        # top pipe height min/max (leave room for gap and ground)
        min_top = 60
        max_top = HEIGHT - GROUND_HEIGHT - PIPE_GAP - 60
        top_h = random.randint(min_top, max_top)

        self.top = pygame.Rect(self.x, 0, PIPE_WIDTH, top_h)
        self.bottom = pygame.Rect(self.x, top_h + PIPE_GAP, PIPE_WIDTH,
                                  HEIGHT - GROUND_HEIGHT - (top_h + PIPE_GAP))

    def move(self):
        self.top.x -= PIPE_SPEED
        self.bottom.x -= PIPE_SPEED

    def recycle_if_needed(self, new_x):
        # If pipe is completely off-screen to the left, move it to new_x and randomize
        if self.top.right < 0:
            self.x = new_x
            self.scored = False
            self.randomize()
            self.top.x = self.x
            self.bottom.x = self.x


def draw_text(screen, text, size, x, y, center=True):
    font = pygame.font.SysFont("Arial", size, bold=True)
    surf = font.render(text, True, (255, 255, 255))
    rect = surf.get_rect()
    if center:
        rect.center = (x, y)
    else:
        rect.topleft = (x, y)
    screen.blit(surf, rect)


def reset_game():
    # Bird as a rectangle
    bird = pygame.Rect(90, HEIGHT // 2 - 20, 34, 24)
    bird_vel = 0.0

    # Create pipes
    pipes = []
    start_x = WIDTH + 120
    for i in range(3):
        pipes.append(PipeSet(start_x + i * PIPE_SPACING))

    score = 0
    game_over = False
    return bird, bird_vel, pipes, score, game_over


def main():
    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("Flappy Bird")
    clock = pygame.time.Clock()

    bird, bird_vel, pipes, score, game_over = reset_game()

    running = True
    while running:
        clock.tick(FPS)

        # ----------------------------
        # EVENTS
        # ----------------------------
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False

                if not game_over and event.key == pygame.K_SPACE:
                    bird_vel = FLAP_STRENGTH

                if game_over and event.key == pygame.K_r:
                    bird, bird_vel, pipes, score, game_over = reset_game()

        # ----------------------------
        # UPDATE
        # ----------------------------
        if not game_over:
            # Apply gravity and move bird
            bird_vel += GRAVITY
            bird.y += int(bird_vel)

            # Bird collisions with top/bottom (ground)
            if bird.top <= 0:
                game_over = True
            if bird.bottom >= HEIGHT - GROUND_HEIGHT:
                game_over = True

            # Move pipes
            for p in pipes:
                p.move()

            # Recycle pipes
            rightmost_x = max(p.top.x for p in pipes)
            for p in pipes:
                p.recycle_if_needed(rightmost_x + PIPE_SPACING)

            # Collision detection + scoring
            for p in pipes:
                if bird.colliderect(p.top) or bird.colliderect(p.bottom):
                    game_over = True

                # Score when bird passes pipe set (once per set)
                if not p.scored and bird.left > p.top.right:
                    p.scored = True
                    score += 1

        # ----------------------------
        # DRAW
        # ----------------------------
        screen.fill((15, 25, 45))  # background

        # Draw ground
        pygame.draw.rect(screen, (30, 80, 30), (0, HEIGHT - GROUND_HEIGHT, WIDTH, GROUND_HEIGHT))

        # Draw pipes
        for p in pipes:
            pygame.draw.rect(screen, (0, 200, 0), p.top)
            pygame.draw.rect(screen, (0, 200, 0), p.bottom)

        # Draw bird
        pygame.draw.rect(screen, (255, 210, 0), bird)

        # Draw score
        draw_text(screen, f"Score: {score}", 26, WIDTH // 2, 40)

        # Game over overlay
        if game_over:
            draw_text(screen, "GAME OVER", 48, WIDTH // 2, HEIGHT // 2 - 40)
            draw_text(screen, "Press R to Restart", 24, WIDTH // 2, HEIGHT // 2 + 20)
            draw_text(screen, "ESC to Quit", 20, WIDTH // 2, HEIGHT // 2 + 55)

        pygame.display.flip()

    pygame.quit()
    sys.exit()


if __name__ == "__main__":
    main()
Run it: Save as flappy_bird.py, install pygame with pip install pygame, then run python flappy_bird.py.