当前位置: 首页 > article >正文

使用Pygame制作“Flappy Bird”游戏

1. 前言

Flappy Bird 是一款“点击上浮松手下落”的横向卷轴游戏:

  • 场景中持续出现上下成对的管道,玩家需要让小鸟在管道之间穿行;
  • 每穿过一对管道记 1 分;
  • 若小鸟碰到管道或掉到地面,则游戏结束;
  • 一旦上手,就会体会到“魔性”且容易让人“上头”的乐趣。

在本篇中,我们使用 Python + Pygame 来从零开始构建一个精简版的 Flappy Bird,主要关注以下要点:

  • 重力/速度:小鸟受“重力”影响不断向下,下落速度随时间加快;
  • 点击上浮:每次按键或点击,即让小鸟速度向上,模拟腾空;
  • 管道生成:随机生成多组上下管道,让它们从右向左移动;
  • 碰撞与得分:检测小鸟是否与管道矩形或地面碰撞;若小鸟成功穿过管道中间的缺口,则得分 +1;
  • 游戏结束:当小鸟撞到管道或地面,游戏停止,显示分数或重来选项。

2. 开发环境

  1. Python 3.x
  2. Pygame 库:若尚未安装,可通过 pip install pygame 来获取。
  3. 图形界面:Windows、macOS、Linux 桌面环境皆可。

确认 import pygame 无报错后,即可开始项目编写。


3. 简要实现思路

  1. 小鸟(Bird)

    • 记录小鸟的 x, y 位置以及 velocity(竖直方向速度);
    • 受“重力”影响,每帧更新时给速度增加一固定值,如 gravity = 0.4,再更新 y 位置;
    • 当按键(如空格)或鼠标点击时,让速度变为负值(如 -6),模拟向上飞。
  2. 管道(Pipe)

    • 管道由上下两根部分组成,中间留有空隙让小鸟通过;
    • 随机生成管道时,可随机空隙位置(上下波动),管道整体从屏幕右侧出现后向左移动。
    • 若管道完全离开屏幕左侧,则可将其移除。
  3. 碰撞检测

    • 小鸟若 y 越过地面(例如 y + bird_height >= 地面高度)或 < 0(飞出屏幕上端),则判定失败;
    • 小鸟若与任意管道矩形重叠,同样判定失败;
    • 可以用 pygame.Rectcolliderect 来检测。
  4. 得分判定

    • 当管道刚好穿过小鸟的 x 位置时,可以认为小鸟成功通过中间的空隙,这时分数 +1。
    • 常见做法是检测小鸟的中心点是否越过某个管道的“通过线”。
  5. 游戏循环

    • 初始化小鸟、管道、分数等;
    • 不断更新(小鸟坐标、管道移动、碰撞检测)和绘制(背景、小鸟、管道、分数);
    • 知道小鸟死亡或玩家退出,结束循环,显示结算画面。

4. 完整示例代码

将以下内容保存为 flappy_bird.py 并运行即可。该示例使用简单的矩形、颜色和文字来模拟 Flappy Bird 的玩法,后期可以替换成更精美的贴图与音效。

import pygame
import sys
import random

# 初始化 Pygame
pygame.init()

# -----------------------
# 全局参数
# -----------------------
WIDTH, HEIGHT = 400, 600
FPS = 60

# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 200, 0)
RED   = (255, 0, 0)

# 窗口与时钟
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Flappy Bird - Pygame 示例")
clock = pygame.time.Clock()

# 字体
font = pygame.font.SysFont("arial", 32)

# 地面高度(小鸟碰到地面即失败)
GROUND_LEVEL = HEIGHT - 50

# -----------------------
# 小鸟类
# -----------------------
class Bird:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.radius = 15
        self.color = RED
        self.velocity = 0  # 垂直速度
        self.gravity = 0.4  # “重力”加速度

    def update(self):
        # 重力作用
        self.velocity += self.gravity
        self.y += self.velocity

    def flap(self):
        # 按键或点击时,让鸟向上冲
        self.velocity = -6

    def draw(self, surface):
        pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), self.radius)

    def get_rect(self):
        # 方便碰撞检测
        return pygame.Rect(self.x - self.radius, self.y - self.radius,
                           self.radius * 2, self.radius * 2)

# -----------------------
# 管道类
# -----------------------
class Pipe:
    def __init__(self, x, gap_y, gap_height=150, width=60):
        """
        x: 管道出现的x坐标
        gap_y: 空隙的中心Y
        gap_height: 空隙的总高度
        width: 管道宽
        """
        self.x = x
        self.width = width
        self.color = GREEN

        self.gap_y = gap_y
        self.gap_height = gap_height

        # 两部分管道的矩形
        # 上管道: 从顶部到 gap_y - gap_height/2
        # 下管道: 从 gap_y + gap_height/2 到地面
        self.top_height = gap_y - gap_height // 2
        self.bottom_y = gap_y + gap_height // 2
        self.speed = 3  # 管道向左移动速度

    def update(self):
        self.x -= self.speed

    def draw(self, surface):
        # 画上管道
        top_rect = pygame.Rect(self.x, 0, self.width, self.top_height)
        pygame.draw.rect(surface, self.color, top_rect)
        # 画下管道
        bottom_rect = pygame.Rect(self.x, self.bottom_y, self.width, HEIGHT - self.bottom_y)
        pygame.draw.rect(surface, self.color, bottom_rect)

    def get_top_rect(self):
        return pygame.Rect(self.x, 0, self.width, self.top_height)

    def get_bottom_rect(self):
        return pygame.Rect(self.x, self.bottom_y, self.width, HEIGHT - self.bottom_y)

    def is_off_screen(self):
        # 如果管道完全移出屏幕左侧
        return self.x + self.width < 0

# -----------------------
# 主函数
# -----------------------
def main():
    bird = Bird(x=100, y=HEIGHT//2)
    pipes = []
    score = 0

    # 首先生成一组初始管道
    for i in range(3):
        gap_y = random.randint(100, GROUND_LEVEL - 100)
        pipe_x = 400 + i * 200  # 每 200 像素出现一对管道
        pipes.append(Pipe(pipe_x, gap_y))

    # 用于检测是否已经计分
    # 当鸟的x > 某个管道的x+width,就说明顺利通过管道 -> score+1
    passed_pipe_indices = set()

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

        # 1) 事件处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN:
                # 点击或按任意键让小鸟向上
                bird.flap()

        # 2) 更新小鸟
        bird.update()

        # 检查小鸟是否撞地面或飞出屏幕上方
        if bird.y + bird.radius >= GROUND_LEVEL or bird.y - bird.radius <= 0:
            running = False

        # 3) 更新管道
        for p in pipes:
            p.update()

        # 管道移除 & 新管道生成
        if pipes and pipes[0].is_off_screen():
            pipes.pop(0)
            # 生成新的管道
            gap_y = random.randint(100, GROUND_LEVEL - 100)
            pipe_x = pipes[-1].x + 200  # 相隔200像素出现新管道
            pipes.append(Pipe(pipe_x, gap_y))

        # 4) 碰撞检测: 小鸟与管道
        bird_rect = bird.get_rect()
        for idx, p in enumerate(pipes):
            if bird_rect.colliderect(p.get_top_rect()) or bird_rect.colliderect(p.get_bottom_rect()):
                running = False

            # 得分判定:如果小鸟已超过管道的右边缘,且还未记分
            if bird.x > p.x + p.width and idx not in passed_pipe_indices:
                score += 1
                passed_pipe_indices.add(idx)

        # 5) 绘制场景
        screen.fill(BLACK)

        # 地面
        pygame.draw.rect(screen, WHITE, (0, GROUND_LEVEL, WIDTH, HEIGHT - GROUND_LEVEL))

        # 管道
        for p in pipes:
            p.draw(screen)

        # 小鸟
        bird.draw(screen)

        # 分数显示
        score_surf = font.render(f"Score: {score}", True, WHITE)
        screen.blit(score_surf, (10, 10))

        pygame.display.flip()

    game_over(score)

def game_over(score):
    # 游戏结束场景
    screen.fill(BLACK)
    msg = f"Game Over! Your Score: {score}"
    label = font.render(msg, True, WHITE)
    rect = label.get_rect(center=(WIDTH // 2, HEIGHT // 2))
    screen.blit(label, rect)
    pygame.display.flip()
    pygame.time.wait(3000)
    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()

代码解析

  1. Bird 类

    • velocity 表示当前的垂直速度,每帧都增加一定重力 gravity
    • update() 用于更新小鸟的 y 坐标;
    • flap()velocity 设置为负值(比如 -6),让小鸟立刻向上冲。
    • 使用 get_rect() 得到小鸟的矩形边界,用于与管道进行 colliderect 碰撞判断。
  2. Pipe 类

    • 包含上下两根管道,通过 gap_ygap_height 确定中间空隙的位置与大小;
    • update() 向左移动管道,若离开屏幕则 is_off_screen() 返回 True 以便移除;
    • get_top_rect()get_bottom_rect() 返回上下管道的 Rect 边界,用于碰撞检测。
  3. 管道队列

    • 程序开始时先生成 3 个管道,让画面中一开始就有管道出现。
    • 当最左侧的管道移出屏幕后,移除它并在最右侧补充一个新管道。
    • 这样,场景就能源源不断地产生新管道。
  4. 得分逻辑

    • 当小鸟 x 坐标超过管道右边 x + width,说明已安全穿过该管道 -> 分数 + 1。
    • 用一个 passed_pipe_indices 集合来记录已经计分的管道,避免重复加分。
  5. 游戏结束

    • 若小鸟超出上下边界或与管道碰撞,立刻退出主循环;
    • 最后进入 game_over() 显示成绩并延时退出。

5. 实现效果

image.png

image.png


6. 总结

本示例通过 Pygame 带你体验了一个简化版的 Flappy Bird 开发流程:

  • 利用重力模拟按键上浮实现小鸟的控制;
  • 管道队列完成场景的动态生成与回收;
  • 借助碰撞检测得分机制让游戏规则更加完善。

如果你能理解并实现这些核心逻辑,相信对于 2D 横版卷轴类游戏的基础也会有了更多体会。你可以进一步在此游戏上进行创意改造,实现更完善、更具可玩性的 Flappy Bird。愿你在游戏编程之旅中越飞越高,创作出属于自己的精彩作品!


http://www.kler.cn/a/526968.html

相关文章:

  • 论文阅读:Realistic Noise Synthesis with Diffusion Models
  • 「 机器人 」利用冲程对称性调节实现仿生飞行器姿态与方向控制
  • MySQL备忘录
  • 在Putty创建php文件
  • 【C语言练习题】整数和实数在计算机中的二进制表示
  • Java创建项目准备工作
  • homebrew-usage
  • Intellij IDEA如何进入初始化页面?
  • AI智慧社区--用户登录JWT令牌校验
  • Vue.js组件开发-实现全屏焦点图片带图标导航按钮控制图片滑动切换
  • 服务器虚拟化实战:架构、技术与最佳实践
  • 使用Pygame制作“打砖块”游戏
  • 二.java开发项目 常用hutool aop mybatisplue依赖2.
  • 分布式架构中的事务管理:需要了解的常见解决方案
  • ai翻唱入门
  • priority_queue的创建_结构体类型(重载小于运算符)c++
  • 计算机网络之计算机网络协议、接口、服务等概念
  • 【MyDB】4-VersionManager 之 2-事务的隔离级别
  • pytorch实现半监督学习
  • CSS入门知识
  • VUE之组件通信(一)
  • win11本地部署 DeepSeek-R1 大模型!免费开源,媲美OpenAI-o1能力,断网也能用
  • 【数据机构】_复杂度
  • 【leetcode详解】T3175(一点反思)
  • arm-linux-gnueabihf安装
  • Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview