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

(图片来源网络,侵删)
下面我将为你提供一个完整、可运行的C语言控制台版Flappy Bird的代码,并详细解释每一部分。
- 平台: 控制台 (Console/Terminal)
- 库: 仅需标准C库 (
stdio.h,conio.h用于键盘输入,windows.h用于控制台设置 - 如果你用的是Windows系统) - 核心机制:
- 一个鸟(用字符表示)受重力影响下落。
- 按空格键,鸟会向上“飞”一小段距离。
- 管道(用字符和
_表示)从右向左移动。 - 鸟必须穿过管道之间的空隙。
- 如果鸟撞到管道或屏幕顶部/底部,游戏结束。
完整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.h和time.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(列),逐个字符地绘制屏幕。 - 绘制逻辑:
- 先判断当前位置是不是鸟的位置 (
if (x == 5 && y == bird_y)),是就画O。 - 再判断是不是管道的位置,是就画。
- 再判断是不是边界,是就画。
- 都不是,就画空格` `。
- 先判断当前位置是不是鸟的位置 (
- 颜色: 在绘制不同元素时,调用
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毫秒,如果没有这个,游戏会运行得飞快,这个延时控制了游戏的帧率。
如何编译和运行
- 你需要一个C语言编译器,GCC (在Linux或MinGW中) 或 Visual Studio。
- 保存代码: 将上面的代码复制并粘贴到一个文件中,命名为
flappy_bird.c。 - 编译:
- 使用GCC (Linux/MinGW/Cygwin):
gcc flappy_bird.c -o flappy_bird
- 使用Visual Studio:
- 创建一个新的 "控制台应用" 项目。
- 将代码粘贴到主
.c文件中。 - 按F5或点击 "本地Windows调试器" 运行。
- 使用GCC (Linux/MinGW/Cygwin):
- 运行: 在控制台中执行生成的可执行文件
flappy_bird。
如何改进和扩展
这个版本是一个很好的起点,你可以尝试添加更多功能:
- 增加难度: 随着分数增加,提高
PIPE_SPEED或减小PIPE_GAP_SIZE。 - 开始界面: 在
main函数中添加一个开始菜单,按任意键才开始游戏。 - 音效: 虽然控制台版很难加音效,但你可以用
printf打印出 "WOOOSH!" 或 "SCORE!" 这样的文字来模拟。 - 更平滑的动画: 目前的动画是跳跃式的,你可以通过更小的
Sleep时间和更小的移动/速度增量来让动画更流畅。 - 数据持久化: 学习文件操作 (
fopen,fprintf,fscanf),将最高分保存到一个文件中,下次游戏时可以读取并显示。
这个项目完美地展示了C语言的强大能力,即使是简单的控制台应用,也能实现非常有趣的游戏逻辑,祝你编码愉快!

(图片来源网络,侵删)
