下面我将为你提供一个基于Windows控制台的简化版Flappy Bird实现,这个版本不依赖任何第三方图形库,只使用C语言标准库和Windows API,非常适合初学者学习和理解。

(图片来源网络,侵删)
项目特点
- 平台: Windows (使用
conio.h和windows.h) - 图形: 在控制台中绘制字符(如
[代表小鸟, 代表管道) - 输入: 使用空格键控制小鸟跳跃
- 核心机制: 重力、跳跃、管道生成与移动、碰撞检测、计分
第一步:准备环境
你需要一个Windows系统下的C语言编译器,
- Visual Studio: 安装后创建一个"控制台应用"项目。
- MinGW (GCC for Windows): 如果你使用的是VS Code或其他编辑器,可以安装MinGW。
- Dev-C++: 一个轻量级的IDE。
重要: 确保你的编译器支持 conio.h 和 windows.h,绝大多数Windows下的编译器都支持。
第二步:完整代码
下面是完整的C语言代码,你可以直接复制到一个 .c 文件中(flappy_bird.c)并编译运行。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h> // 用于 _kbhit() 和 _getch()
#include <windows.h> // 用于 Sleep() 和控制台光标操作
#include <time.h> // 用于 srand() 和 time()
// 控制台窗口的宽度和高度
#define WIDTH 60
#define HEIGHT 20
// 游戏对象
int bird_y = HEIGHT / 2; // 小鸟的垂直位置
int bird_velocity = 0; // 小鸟的速度
int pipe_x = WIDTH; // 管道的水平位置
int pipe_gap_y = HEIGHT / 2; // 管道缺口中心的垂直位置
int pipe_gap_size = 5; // 管道缺口的大小
int score = 0; // 玩家得分
int game_over = 0; // 游戏结束标志
// 函数声明
void gotoxy(int x, int y);
void draw_game();
void update_game();
void reset_game();
int main() {
// 初始化随机数种子
srand((unsigned int)time(NULL));
// 隐藏控制台光标
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(hConsole, &cursorInfo);
cursorInfo.bVisible = 0;
SetConsoleCursorInfo(hConsole, &cursorInfo);
// 游戏主循环
while (1) {
if (!game_over) {
// 检查是否有按键
if (_kbhit()) {
char key = _getch();
if (key == ' ') { // 空格键跳跃
bird_velocity = -2;
}
}
update_game();
draw_game();
Sleep(100); // 控制游戏速度
} else {
// 游戏结束,显示最终得分和重新开始提示
draw_game();
gotoxy(WIDTH / 2 - 10, HEIGHT / 2);
printf("Game Over! Score: %d", score);
gotoxy(WIDTH / 2 - 12, HEIGHT / 2 + 1);
printf("Press 'R' to Restart or 'Q' to Quit");
if (_kbhit()) {
char key = _getch();
if (key == 'r' || key == 'R') {
reset_game();
} else if (key == 'q' || key == 'Q') {
break; // 退出游戏循环
}
}
}
}
return 0;
}
// 将光标移动到控制台的指定位置
void gotoxy(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
// 绘制游戏画面
void draw_game() {
system("cls"); // 清屏
// 绘制上边界
for (int i = 0; i < WIDTH; i++) {
printf("-");
}
printf("\n");
// 绘制游戏主体区域
for (int y = 1; y < HEIGHT - 1; y++) {
for (int x = 0; x < WIDTH; x++) {
if (x == 0 || x == WIDTH - 1) {
printf("|"); // 左右边界
} else if (x == 3 && y == bird_y) {
printf("B"); // 小鸟
} else if (x == pipe_x) {
// 绘制管道
if (y < pipe_gap_y - pipe_gap_size / 2 || y > pipe_gap_y + pipe_gap_size / 2) {
printf("|");
}
} else {
printf(" ");
}
}
printf("\n");
}
// 绘制下边界
for (int i = 0; i < WIDTH; i++) {
printf("-");
}
printf("\n");
// 显示得分
gotoxy(WIDTH - 10, 0);
printf("Score: %d", score);
}
// 更新游戏逻辑
void update_game() {
// 更新小鸟位置 (简单的物理模拟)
bird_velocity += 0.5; // 重力加速度
bird_y += (int)bird_velocity;
// 更新管道位置
pipe_x--;
// 检查是否通过管道
if (pipe_x == 3) {
score++;
}
// 如果管道移出屏幕,生成新的管道
if (pipe_x < 0) {
pipe_x = WIDTH;
// 随机生成新的缺口位置
pipe_gap_y = 3 + rand() % (HEIGHT - 6);
}
// 碰撞检测
// 1. 撞到上下边界
if (bird_y <= 0 || bird_y >= HEIGHT - 1) {
game_over = 1;
}
// 2. 撞到管道
if (pipe_x == 3 && (bird_y <= pipe_gap_y - pipe_gap_size / 2 || bird_y >= pipe_gap_y + pipe_gap_size / 2)) {
game_over = 1;
}
}
// 重置游戏状态
void reset_game() {
bird_y = HEIGHT / 2;
bird_velocity = 0;
pipe_x = WIDTH;
pipe_gap_y = HEIGHT / 2;
score = 0;
game_over = 0;
}
第三步:代码详解
头文件和宏定义
#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <windows.h> #include <time.h> #define WIDTH 60 #define HEIGHT 20
stdio.h: 标准输入输出,用于printf。stdlib.h: 标准库,用于system("cls")清屏和rand()/srand()。conio.h: 控制台输入输出,提供_kbhit()(检测是否有按键)和_getch()(获取一个按键)。windows.h: Windows API,提供Sleep()(延时)和SetConsoleCursorPosition()(移动光标)。time.h: 用于time()来初始化随机数生成器,让每次游戏的管道位置都不同。WIDTH和HEIGHT: 定义了游戏画布的大小。
游戏状态变量
int bird_y = HEIGHT / 2; int bird_velocity = 0; int pipe_x = WIDTH; int pipe_gap_y = HEIGHT / 2; int pipe_gap_size = 5; int score = 0; int game_over = 0;
这些全局变量存储了游戏中所有对象的状态。bird_y 是小鸟的Y坐标,bird_velocity 是它的速度,pipe_x 是管道的X坐标。
main 函数 - 游戏主循环
int main() {
// ... 初始化代码 ...
while (1) {
if (!game_over) {
// 游戏进行中
if (_kbhit()) { /* 处理输入 */ }
update_game(); // 更新逻辑
draw_game(); // 绘制画面
Sleep(100); // 控制帧率
} else {
// 游戏结束
// ... 显示结束信息,处理重新开始 ...
}
}
return 0;
}
这是游戏的核心,一个 while(1) 无限循环持续运行,直到玩家选择退出。
- 游戏进行中: 不断检测输入、更新游戏状态、重绘画面。
- 游戏结束: 显示最终得分,并等待玩家按 'R' 重新开始或 'Q' 退出。
gotoxy 函数
void gotoxy(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
这个函数是控制台绘图的基石,它允许我们像在图形界面中一样,将光标精确地移动到 (x, y) 坐标,然后在那里打印字符,从而实现“画”出游戏画面的效果。
draw_game 函数
void draw_game() {
system("cls"); // 清屏
// ... 使用双层循环遍历整个画布 ...
// 根据游戏状态变量,在特定位置打印特定字符 ...
}
system("cls"): 每次绘制前清空整个屏幕,为下一帧做准备。- 双层循环: 外层循环遍历行(Y坐标),内层循环遍历列(X坐标),这是在控制台中绘制2D画面的标准方法。
- 条件判断: 在内层循环中,通过判断当前坐标
(x, y)是否与小鸟、管道等对象的位置匹配,来决定打印什么字符(B, , 空格等)。
update_game 函数
void update_game() {
// 更新小鸟位置
bird_velocity += 0.5; // 重力
bird_y += (int)bird_velocity;
// 更新管道位置
pipe_x--;
// 检查通过管道得分
// 检查管道是否移出屏幕并重置
// 碰撞检测
}
这是游戏逻辑的核心,它负责:
- 物理模拟: 通过增加速度(重力)来更新小鸟的位置。
- 移动对象: 将管道向左移动(
pipe_x--)。 - 碰撞检测: 检查小鸟是否撞到了边界或管道,如果是,则设置
game_over = 1。
reset_game 函数
void reset_game() {
// 将所有游戏状态变量重置为初始值
bird_y = HEIGHT / 2;
bird_velocity = 0;
// ...
game_over = 0;
}
当玩家选择重新开始时,调用此函数将所有变量恢复到初始状态,实现“重新开始”的功能。
如何运行和改进
- 编译运行: 将代码保存为
flappy_bird.c,用你的编译器编译并运行。 - 改进方向:
- 更平滑的动画: 将
Sleep(100)调小,并相应地调整速度和重力参数,可以让游戏更流畅。 - 更好的图形: 使用不同的ASCII字符(如 , , )来让小鸟和管道看起来更美观。
- 难度递增: 随着得分增加,可以减小
pipe_gap_size或增加管道的移动速度。 - 音效: Windows API可以播放简单的系统音效,可以在得分或碰撞时加入。
- 面向对象重构: 将小鸟、管道等封装成结构体,用结构体数组来管理多个管道,使代码更结构化。
- 更平滑的动画: 将
这个项目是学习C语言和游戏逻辑的绝佳起点,祝你玩得开心!
