C语言如何实现Flappy Bird游戏?

99ANYc3cd6
预计阅读时长 32 分钟
位置: 首页 C语言 正文

由于用纯C语言(没有图形库如SDL)来制作图形游戏非常复杂,通常我们会在控制台(黑窗口)中实现一个简化版的Flappy Bird,这个版本的核心玩法和逻辑是完全一样的。

c语言 flappy bird
(图片来源网络,侵删)

下面我将为你提供一个完整、可运行的C语言控制台版Flappy Bird的代码,并详细解释每一部分。

  • 平台: 控制台 (Console/Terminal)
  • : 仅需标准C库 (stdio.h, conio.h 用于键盘输入, windows.h 用于控制台设置 - 如果你用的是Windows系统)
  • 核心机制:
    1. 一个鸟(用字符表示)受重力影响下落。
    2. 按空格键,鸟会向上“飞”一小段距离。
    3. 管道(用字符和_表示)从右向左移动。
    4. 鸟必须穿过管道之间的空隙。
    5. 如果鸟撞到管道或屏幕顶部/底部,游戏结束。

完整C语言代码

#include <stdio.h>
#include <conio.h>      // 用于 _kbhit() 和 _getch()
#include <windows.h>    // 用于 Sleep(), GetStdHandle(), SetConsoleTextAttribute()
#include <stdlib.h>     // 用于 rand(), srand()
#include <time.h>       // 用于 time()
// --- 游戏配置 ---
#define SCREEN_WIDTH 60
#define SCREEN_HEIGHT 20
#define BIRD_CHAR 'O'
#define PIPE_CHAR '|'
#define PIPE_GAP_SIZE 5
#define PIPE_WIDTH 2
#define GRAVITY 1
#define JUMP_STRENGTH -5
#define PIPE_SPEED 2
// --- 游戏状态 ---
int bird_y;
int bird_velocity;
int pipes_x[5]; // 存储管道的x坐标
int pipes_holes_y[5]; // 存储每个管道空隙的y坐标
int score;
int game_over;
// --- 控制台光标设置 ---
void hideCursor() {
    HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(consoleHandle, &cursorInfo);
    cursorInfo.bVisible = FALSE;
    SetConsoleCursorInfo(consoleHandle, &cursorInfo);
}
// --- 设置控制台颜色 ---
void setColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
// --- 初始化游戏 ---
void initGame() {
    bird_y = SCREEN_HEIGHT / 2;
    bird_velocity = 0;
    score = 0;
    game_over = 0;
    // 初始化管道位置和空隙
    srand(time(NULL)); // 设置随机数种子
    for (int i = 0; i < 5; i++) {
        pipes_x[i] = SCREEN_WIDTH + i * 15; // 管道间距
        pipes_holes_y[i] = 3 + rand() % (SCREEN_HEIGHT - PIPE_GAP_SIZE - 6); // 随机空隙位置
    }
}
// --- 更新游戏状态 ---
void update() {
    if (game_over) return;
    // 1. 更新鸟的位置
    bird_velocity += GRAVITY;
    bird_y += bird_velocity;
    // 2. 更新管道位置
    for (int i = 0; i < 5; i++) {
        pipes_x[i] -= PIPE_SPEED;
        // 如果管道移出屏幕,将其移到最右边并生成新的空隙
        if (pipes_x[i] < -PIPE_WIDTH) {
            pipes_x[i] = SCREEN_WIDTH;
            pipes_holes_y[i] = 3 + rand() % (SCREEN_HEIGHT - PIPE_GAP_SIZE - 6);
        }
        // 3. 检测碰撞
        // 检测是否通过管道
        if (pipes_x[i] == 5 && bird_x == 5) { // 假设鸟在x=5的位置
            score++;
        }
        // 检测是否撞到管道
        if (bird_x >= pipes_x[i] && bird_x <= pipes_x[i] + PIPE_WIDTH) {
            if (bird_y <= pipes_holes_y[i] || bird_y >= pipes_holes_y[i] + PIPE_GAP_SIZE) {
                game_over = 1;
            }
        }
    }
    // 4. 检测是否撞到顶部或底部
    if (bird_y <= 0 || bird_y >= SCREEN_HEIGHT - 1) {
        game_over = 1;
    }
}
// --- 渲染游戏画面 ---
void render() {
    // 清屏 (Windows系统)
    system("cls");
    // --- 绘制游戏画面 ---
    for (int y = 0; y < SCREEN_HEIGHT; y++) {
        for (int x = 0; x < SCREEN_WIDTH; x++) {
            // 绘制鸟 (假设鸟在x=5的位置)
            if (x == 5 && y == bird_y) {
                setColor(14); // 黄色
                printf("%c", BIRD_CHAR);
                setColor(7);  // 恢复默认颜色
                continue;
            }
            // 绘制管道
            int isPipe = 0;
            for (int i = 0; i < 5; i++) {
                if (x >= pipes_x[i] && x <= pipes_x[i] + PIPE_WIDTH - 1) {
                    if (y < pipes_holes_y[i] || y >= pipes_holes_y[i] + PIPE_GAP_SIZE) {
                        isPipe = 1;
                        break;
                    }
                }
            }
            if (isPipe) {
                setColor(10); // 绿色
                printf("%c", PIPE_CHAR);
                setColor(7);  // 恢复默认颜色
                continue;
            }
            // 绘制边界
            if (y == 0 || y == SCREEN_HEIGHT - 1) {
                setColor(15); // 白色
                printf("-");
                setColor(7);
                continue;
            }
            // 空白处
            printf(" ");
        }
        printf("\n");
    }
    // --- 绘制UI ---
    setColor(11); // 青色
    printf("Score: %d\n", score);
    if (game_over) {
        setColor(12); // 红色
        printf("GAME OVER! Press 'R' to restart or 'Q' to quit.");
    } else {
        setColor(14); // 黄色
        printf("Press SPACE to jump!");
    }
    setColor(7); // 恢复默认颜色
}
// --- 主游戏循环 ---
int main() {
    hideCursor();
    initGame();
    while (1) {
        render();
        // 检测按键输入
        if (_kbhit()) {
            char key = _getch();
            if (key == ' ' && !game_over) { // 空格键跳跃
                bird_velocity = JUMP_STRENGTH;
            } else if ((key == 'r' || key == 'R') && game_over) { // R键重启
                initGame();
            } else if (key == 'q' || key == 'Q') { // Q键退出
                break;
            }
        }
        update();
        Sleep(50); // 控制游戏速度
    }
    return 0;
}

代码详解

头文件和宏定义

#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#define SCREEN_WIDTH 60
#define SCREEN_HEIGHT 20
// ...
  • stdio.h: 标准输入输出,用于 printf
  • conio.h: 非标准库,但在Windows下的编译器(如VS, MinGW)中很常见,用于 _kbhit()(检测是否有按键)和 _getch()(获取一个按键,不显示在屏幕上)。
  • windows.h: Windows API,用于 Sleep()(延时)、GetStdHandle()SetConsoleTextAttribute()(改变控制台文字颜色)。
  • stdlib.htime.h: 用于 rand()(生成随机数)和 srand()(设置随机数种子)。
  • #define: 定义常量,方便修改游戏参数,如屏幕大小、重力、跳跃力度等。

游戏状态变量

int bird_y;
int bird_velocity;
int pipes_x[5];
int pipes_holes_y[5];
int score;
int game_over;
  • bird_y: 鸟的垂直位置,因为我们只在垂直方向移动,所以不需要bird_x,可以把它固定在屏幕的某一列(比如第5列)。
  • bird_velocity: 鸟的速度,这是实现“跳跃”和“重力”的关键,重力会让速度不断增加,而跳跃会瞬间给一个向上的负速度。
  • pipes_x[5]pipes_holes_y[5]: 使用数组来管理多个管道,我们用一个循环来更新和绘制它们,当管道移出屏幕后,就重置到最右边,形成连续不断的管道。
  • score: 得分。
  • game_over: 游戏结束标志。

辅助函数

  • hideCursor(): 调用Windows API隐藏控制台闪烁的光标,让画面更干净。
  • setColor(int color): 封装了改变文字颜色的功能,让代码更简洁。

initGame() - 初始化

void initGame() {
    bird_y = SCREEN_HEIGHT / 2;
    bird_velocity = 0;
    score = 0;
    game_over = 0;
    // ...
    srand(time(NULL)); // 只在游戏开始时设置一次种子
    // ...
}
  • 这个函数在游戏开始和重新开始时被调用。
  • 将鸟的位置、速度、分数等重置为初始值。
  • srand(time(NULL)) 确保每次运行游戏时,管道的随机位置都不同。

update() - 核心逻辑

这是游戏的心脏,负责计算所有物体的新位置和检测碰撞。

  • 更新鸟: bird_velocity += GRAVITY; bird_y += bird_velocity; 这两行代码模拟了物理效果,重力不断给向下的加速度,鸟的位置根据速度更新。
  • 更新管道: pipes_x[i] -= PIPE_SPEED; 让管道向左移动。
  • 管道循环: 当管道移出屏幕左侧 (pipes_x[i] < -PIPE_WIDTH),我们把它重置到最右边 (SCREEN_WIDTH),并给它一个新的随机空隙位置,这样就实现了无限循环的管道。
  • 碰撞检测:
    • 边界: if (bird_y <= 0 || bird_y >= SCREEN_HEIGHT - 1) 检查鸟是否飞出屏幕顶部或触底。
    • 管道: if (bird_x >= pipes_x[i] && bird_x <= pipes_x[i] + PIPE_WIDTH) 判断鸟是否在管道的x范围内,如果是,再判断bird_y是否在管道的空隙之外 (bird_y <= pipes_holes_y[i] || bird_y >= pipes_holes_y[i] + PIPE_GAP_SIZE)。
    • 如果发生碰撞,game_over = 1;
  • 计分: 当鸟成功通过一个管道时(当管道的x坐标到达鸟的x坐标时),score++

render() - 渲染画面

  • 清屏: system("cls"); 清空整个控制台,为新一帧的画面做准备。
  • 双重循环: 外层循环遍历y(行),内层循环遍历x(列),逐个字符地绘制屏幕。
  • 绘制逻辑:
    1. 先判断当前位置是不是鸟的位置 (if (x == 5 && y == bird_y)),是就画O
    2. 再判断是不是管道的位置,是就画。
    3. 再判断是不是边界,是就画。
    4. 都不是,就画空格` `。
  • 颜色: 在绘制不同元素时,调用setColor()来改变颜色,使画面更生动。
  • UI: 在画面最后,用printf显示当前分数和操作提示。

main() - 游戏循环

int main() {
    hideCursor();
    initGame();
    while (1) { // 无限循环
        render();
        // ... 按键检测 ...
        update();
        Sleep(50);
    }
    return 0;
}
  • 这是标准的游戏主循环结构。
  • 渲染 -> 输入 -> 更新 -> 延时,这个循环不断重复,构成了游戏的动态过程。
  • render(): 先画出当前画面。
  • _kbhit(): 检查玩家是否按下了键。
  • _getch(): 如果按了键,获取是哪个键。
    • 空格键:让鸟向上跳(bird_velocity = JUMP_STRENGTH;)。
    • R键:游戏结束后重新开始(调用initGame())。
    • Q键:退出游戏(break跳出while(1)循环)。
  • update(): 根据当前状态更新游戏逻辑。
  • Sleep(50): 暂停50毫秒,如果没有这个,游戏会运行得飞快,这个延时控制了游戏的帧率。

如何编译和运行

  1. 你需要一个C语言编译器,GCC (在Linux或MinGW中) 或 Visual Studio。
  2. 保存代码: 将上面的代码复制并粘贴到一个文件中,命名为 flappy_bird.c
  3. 编译:
    • 使用GCC (Linux/MinGW/Cygwin):
      gcc flappy_bird.c -o flappy_bird
    • 使用Visual Studio:
      1. 创建一个新的 "控制台应用" 项目。
      2. 将代码粘贴到主 .c 文件中。
      3. 按F5或点击 "本地Windows调试器" 运行。
  4. 运行: 在控制台中执行生成的可执行文件 flappy_bird

如何改进和扩展

这个版本是一个很好的起点,你可以尝试添加更多功能:

  1. 增加难度: 随着分数增加,提高PIPE_SPEED或减小PIPE_GAP_SIZE
  2. 开始界面: 在main函数中添加一个开始菜单,按任意键才开始游戏。
  3. 音效: 虽然控制台版很难加音效,但你可以用 printf 打印出 "WOOOSH!" 或 "SCORE!" 这样的文字来模拟。
  4. 更平滑的动画: 目前的动画是跳跃式的,你可以通过更小的Sleep时间和更小的移动/速度增量来让动画更流畅。
  5. 数据持久化: 学习文件操作 (fopen, fprintf, fscanf),将最高分保存到一个文件中,下次游戏时可以读取并显示。

这个项目完美地展示了C语言的强大能力,即使是简单的控制台应用,也能实现非常有趣的游戏逻辑,祝你编码愉快!

c语言 flappy bird
(图片来源网络,侵删)
-- 展开阅读全文 --
头像
数据库不乱码 织梦乱码
« 上一篇 01-14
c语言程序设计教程第三版答案在哪里找?
下一篇 » 01-14

相关文章

取消
微信二维码
支付宝二维码

目录[+]