C++小游戏 之 Flappy Bird
成功运行的游戏效果如上图所示。
想象一下我们要做的游戏:
- 有一只小鸟(就像一个 “0” 字符,带着翅膀)
- 小鸟要穿过一些管道(像这样 ”|==|”)
- 每通过一个管道就能得1分
- 小鸟会自动往下掉,按上键可以让它往上飞
就像玩具一样,我们需要先准备好所有的”零件”,然后一步步把它们组装起来。下面,让我们来详细地一步步编程实现它。让刚学编程的小朋友也能理解。
第1步:搭建基础框架
新建一个源代码文件:flappybird.cpp
,内容如下:
#include <ncurses.h> // 控制终端显示的库
#include <unistd.h> // 提供usleep函数
int main() {
initscr(); // 初始化ncurses
raw(); // 直接获取键盘输入
noecho(); // 不显示输入字符
curs_set(0); // 隐藏光标
// 游戏主循环将在这里
endwin(); // 结束ncurses
return 0;
}
编译时需要加-lncurses
参数,例如:
g++ flappybird.cpp -o game -lncurses`
-lncurses
的意思, 是链接 ncurses
依赖库,如果系统中没有ncurses
库,需要执行如下命令安装:
sudo apt install libncurses-dev
第2步:创建游戏窗口
const int NUM_ROWS = 24; // 窗口高度
const int NUM_COLS = 80; // 窗口宽度
int main() {
// ...之前初始化代码...
// 设置窗口尺寸(需要实际终端支持)
resizeterm(NUM_ROWS, NUM_COLS);
// ...其他代码...
}
第3步:绘制地板和天花板
void draw_floor() {
for(int i=0; i<NUM_COLS; i++) {
mvaddch(NUM_ROWS-1, i, '-'); // 最后一行画地板
mvaddch(0, i, '-'); // 第一行画天花板
}
}
int main() {
// ...初始化后...
draw_floor();
refresh(); // 刷新显示
getch(); // 等待按键
}
第4步:创建小鸟结构
struct Flappy {
int y; // 当前高度
int speed; // 下落速度
};
int main() {
Flappy bird {NUM_ROWS/2, 1};
mvaddch(bird.y, 10, '>'); // 在第10列显示小鸟
refresh();
getch();
}
第5步:实现重力效果
void update_bird(Flappy &b) {
b.speed += 1; // 重力加速度
b.y += b.speed; // 更新位置
if(b.y < 0) b.y = 0;
if(b.y >= NUM_ROWS-1) b.y = NUM_ROWS-2;
}
int main() {
Flappy bird {NUM_ROWS/2, 0};
while(true) {
clear();
update_bird(bird);
mvaddch(bird.y, 10, '>');
refresh();
usleep(100000); // 0.1秒延迟
}
}
第6步:添加跳跃控制
int main() {
// ...初始化...
while(true) {
int ch = getch();
if(ch == ' ') { // 按空格跳跃
bird.speed = -3; // 向上速度
}
// ...其他代码...
}
}
第7步:创建管道结构
struct Pipe {
int x; // 水平位置
int gap_y; // 缺口位置
bool passed; // 是否已通过
};
Pipe create_pipe() {
return {NUM_COLS, rand()%(NUM_ROWS-6)+3, false};
}
第8步:绘制管道
void draw_pipe(Pipe p) {
// 上管道
for(int y=0; y<p.gap_y-2; y++)
mvaddch(y, p.x, '|');
// 下管道
for(int y=p.gap_y+2; y<NUM_ROWS-1; y++)
mvaddch(y, p.x, '|');
}
第9步:移动管道
void move_pipes(Pipe pipes[], int count) {
for(int i=0; i<count; i++) {
pipes[i].x--;
if(pipes[i].x < -5) // 移出屏幕后重置
pipes[i] = create_pipe();
}
}
第10步:碰撞检测
bool check_collision(Flappy b, Pipe p) {
if(p.x != 10) return false; // 小鸟在第10列
return (b.y < p.gap_y-2 || b.y > p.gap_y+2);
}
第11步:计分系统
int score = 0;
void update_score(Pipe pipes[], int count) {
for(int i=0; i<count; i++) {
if(pipes[i].x == 9 && !pipes[i].passed) {
score++;
pipes[i].passed = true;
}
}
}
第12步:游戏主循环结构
int main() {
Pipe pipes[2] = {create_pipe(), create_pipe()};
pipes[1].x += 20; // 第二个管道的位置偏移
while(true) {
// 处理输入
// 更新位置
// 检测碰撞
// 绘制画面
usleep(1000000/30); // 30帧/秒
}
}
第13步:游戏结束画面
void game_over() {
clear();
mvprintw(NUM_ROWS/2, NUM_COLS/2-5, "游戏结束!");
mvprintw(NUM_ROWS/2+1, NUM_COLS/2-8, "得分: %d", score);
refresh();
getch();
endwin();
exit(0);
}
第14步:添加启动画面
void splash_screen() {
mvprintw(NUM_ROWS/2-3, NUM_COLS/2-10, "FLAPPY BIRD");
mvprintw(NUM_ROWS/2, NUM_COLS/2-12, "按空格键开始游戏");
refresh();
while(getch() != ' '); // 等待空格键
}
第15步:最终整合
// 包含所有上述功能的完整代码
// (由于篇幅限制,这里展示主循环结构)
int main() {
// 初始化...
splash_screen();
Flappy bird {NUM_ROWS/2, 0};
Pipe pipes[2] = {create_pipe(), create_pipe()};
pipes[1].x += 20;
while(true) {
// 处理输入
// 更新物理
// 检测碰撞
// 绘制画面
if(碰撞检测) game_over();
}
}
完整代码的实现需要将上述所有步骤组合起来。这个游戏涉及的主要知识点:
- ncurses 库:用于终端图形界面
- 结构体:组织游戏对象的数据
- 游戏循环:处理输入 → 更新状态 → 渲染画面的循环
- 物理模拟:简单的重力加速度实现
- 碰撞检测:判断小鸟和管道的位置关系
- 状态管理:游戏进行、结束、重启等状态转换
建议你按照以下顺序逐步实现:
- 先让小鸟能上下移动
- 添加静止的管道
- 让管道移动
- 添加碰撞检测
- 最后完善计分和界面
遇到问题可以随时问我,比如:
- “为什么小鸟不显示?”
- “管道移动速度怎么调整?”
- “碰撞检测不准确怎么办?”
要编译运行这个游戏,记得使用:
g++ -o flappy flappy.cpp -lncurses