C语言Flappy Bird如何实现核心碰撞检测?

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

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

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

项目特点

  • 平台: Windows (使用 conio.hwindows.h)
  • 图形: 在控制台中绘制字符(如 [ 代表小鸟, 代表管道)
  • 输入: 使用空格键控制小鸟跳跃
  • 核心机制: 重力、跳跃、管道生成与移动、碰撞检测、计分

第一步:准备环境

你需要一个Windows系统下的C语言编译器,

  • Visual Studio: 安装后创建一个"控制台应用"项目。
  • MinGW (GCC for Windows): 如果你使用的是VS Code或其他编辑器,可以安装MinGW。
  • Dev-C++: 一个轻量级的IDE。

重要: 确保你的编译器支持 conio.hwindows.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() 来初始化随机数生成器,让每次游戏的管道位置都不同。
  • WIDTHHEIGHT: 定义了游戏画布的大小。

游戏状态变量

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;
}

当玩家选择重新开始时,调用此函数将所有变量恢复到初始状态,实现“重新开始”的功能。


如何运行和改进

  1. 编译运行: 将代码保存为 flappy_bird.c,用你的编译器编译并运行。
  2. 改进方向:
    • 更平滑的动画: 将 Sleep(100) 调小,并相应地调整速度和重力参数,可以让游戏更流畅。
    • 更好的图形: 使用不同的ASCII字符(如 , , )来让小鸟和管道看起来更美观。
    • 难度递增: 随着得分增加,可以减小 pipe_gap_size 或增加管道的移动速度。
    • 音效: Windows API可以播放简单的系统音效,可以在得分或碰撞时加入。
    • 面向对象重构: 将小鸟、管道等封装成结构体,用结构体数组来管理多个管道,使代码更结构化。

这个项目是学习C语言和游戏逻辑的绝佳起点,祝你玩得开心!

-- 展开阅读全文 --
头像
C语言error type有哪些常见类型?
« 上一篇 03-05
织梦后台上传不了图片
下一篇 » 03-05

相关文章

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

目录[+]