恐龙跳跃游戏(main-副本.cpp)实现全过程
参考
VSCode配置c/c++环境-01-DevC++配置MinGW-w64 8.1.0 编译器
VSCode配置c/c++环境-02-VSCode配置MinGW-w64 8.1.0 编译器

基础环境
开发工具为VSCode
编译器为:D:\software\Dev-Cpp492\MinGW64\bin\g++.exe
c_cpp_properties.json内容如下:
bash
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"./ege/include"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"compilerPath": "D:\\software\\Dev-Cpp492\\MinGW64\\bin\\g++.exe",
"cStandard": "c11",
"cppStandard": "c++11",
"intelliSenseMode": "windows-gcc-x64"
}
],
"version": 4
}
tasks.json内容如下:
json
{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++.exe 生成活动文件",
"command": "D:\\software\\Dev-Cpp492\\MinGW64\\bin\\g++.exe",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe",
//下面这些参数是要自己写的,至于怎么找到的看下面的图片及介绍
"-std=c++11",
"-ID:\\code\\EGE_Test_vscode\\ege\\include", // 这里的.ege/include 是当前目录下的ege/include 目录,注意要用这种形式,不要用绝对路径 对应头文件的路径
"-LD:\\code\\EGE_Test_vscode\\ege\\lib", // 对应库文件的路径
"-lgraphics64", //32位的可能要把64去掉,具体自己尝试
"-luuid",
"-lmsimg32",
"-lgdi32",
"-limm32",
"-lole32",
"-loleaut32"
],
"options": {
"cwd": "D:\\software\\Dev-Cpp492\\MinGW64\\bin"
},
"problemMatcher": [
"$gcc"
],
"group": "build",
"detail": "编译器: D:\\software\\Dev-Cpp492\\MinGW64\\bin\\g++.exe"
}
]
}
一、需求分析
1. 核心需求
开发一款基于C++和EGE图形库的2D恐龙跳跃跑酷游戏,核心目标是让玩家控制恐龙跳跃躲避障碍物,通过游戏性设计(随机障碍物速度、分数累计)提升趣味性,同时保证基础的游戏闭环(开始、运行、结束、重启逻辑)。
2. 功能需求拆解
| 功能模块 | 具体需求 |
|---|---|
| 图形界面 | 800×400像素窗口,白色背景,地面可视化,分数/游戏结束文字显示 |
| 恐龙控制 | 初始位置固定,支持空格键/上方向键触发跳跃,跳跃有上升/下落物理逻辑 |
| 障碍物系统 | 从右侧生成,向左移动(速度6-9随机),超出左侧屏幕后重置位置和速度 |
| 碰撞检测 | 恐龙与障碍物碰撞时游戏结束 |
| 分数系统 | 障碍物每完成一次屏幕穿越,分数+1,实时显示在窗口右上角 |
| 游戏状态管理 | 支持游戏结束判定,结束后显示"Game Over",可重置状态重新开始(基础逻辑) |
| 帧率控制 | 约60FPS(帧延迟16ms),保证游戏运行流畅 |
3. 非功能需求
- 基于EGE图形库开发,兼容C++基础语法;
- 随机数种子初始化,保证障碍物速度随机性;
- 碰撞检测算法简洁高效(AABB轴对齐检测);
- 输入响应及时,无明显延迟。
二、概要设计
1. 架构设计(模块化拆分)
采用"面向结构体+函数"的模块化设计(C++类的简化实现),核心模块划分如下:
| 模块 | 核心职责 |
|---|---|
| 恐龙模块(Dino) | 封装恐龙坐标、跳跃状态,提供跳跃触发、位置更新、绘制方法 |
| 障碍物模块 | 封装障碍物坐标、速度,提供位置更新、重置、绘制方法 |
| 游戏状态模块 | 封装分数、游戏结束状态,提供分数更新、状态重置方法 |
| 绘制模块 | 独立的背景、分数、游戏结束文字绘制函数 |
| 输入模块 | 独立的按键检测与跳跃触发函数 |
| 碰撞检测模块 | 独立的AABB碰撞检测函数 |
| 游戏循环模块 | 整合输入、更新、绘制、碰撞检测的核心循环 |
| 初始化/清理模块 | 窗口创建、随机数初始化、资源清理函数 |
2. 核心流程设计
初始化游戏(窗口+随机数)→ 创建游戏对象(恐龙/障碍物/游戏状态)→ 进入游戏循环:
↓
处理输入(检测按键→触发跳跃)→ 更新恐龙/障碍物位置 → 检测障碍物是否重置(重置则加分)→
↓
绘制所有元素(背景+恐龙+障碍物+分数)→ 碰撞检测(碰撞则游戏结束)→ 帧率控制 →
↓
游戏结束 → 显示Game Over → 等待按键 → 清理资源
三、详细设计(功能点+测试)
cpp
/**
* 恐龙跳跃游戏
* 简单的 2D 跑酷游戏,玩家控制恐龙跳跃躲避障碍物
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 随机数函数
#include <time.h> // 时间函数
#include <cstdio> // 格式化输出
1. 常量定义(基础配置)
| 常量名 | 取值 | 设计目的 | 测试点 |
|---|---|---|---|
| WINDOW_WIDTH/HEIGHT | 800/400 | 固定游戏窗口尺寸 | 运行程序,窗口尺寸是否为800×400 |
| GROUND_Y | 350 | 地面绘制基准线 | 背景绘制后,地面线是否在Y=350位置 |
| JUMP_HEIGHT/SPEED | 100/5 | 控制跳跃高度和流畅度 | 按下空格,恐龙是否上升100像素后下落 |
| OBSTACLE参数 | 宽/高30,Y=320 | 障碍物尺寸与地面贴合 | 障碍物绘制后,是否在Y=320位置(不穿透地面) |
| FRAME_DELAY | 16 | 控制60FPS帧率 | 游戏运行时无明显卡顿,帧率稳定 |
cpp
// ============ 常量定义 ============
const int WINDOW_WIDTH = 800; // 窗口宽度
const int WINDOW_HEIGHT = 400; // 窗口高度
const int GROUND_Y = 350; // 地面 Y 坐标
const int DINO_WIDTH = 50; // 恐龙宽度
const int DINO_HEIGHT = 50; // 恐龙高度
const int DINO_X = 50; // 恐龙初始 X 坐标
const int DINO_GROUND_Y = 300; // 恐龙地面 Y 坐标
const int JUMP_HEIGHT = 100; // 跳跃高度
const int JUMP_SPEED = 5; // 跳跃/下落速度
const int OBSTACLE_WIDTH = 30; // 障碍物宽度
const int OBSTACLE_HEIGHT = 30; // 障碍物高度
const int OBSTACLE_Y = 320; // 障碍物 Y 坐标
const int OBSTACLE_START_X = 800; // 障碍物起始 X 坐标
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
2. 恐龙模块(Dino结构体)
功能点1:跳跃逻辑
- 设计:在地面(!is_jump)时或空中时能触发跳跃,设置is_jump=true并初始化jump_h=JUMP_HEIGHT;更新阶段,上升时Y递减,jump_h递减至0后结束上升,下落时Y递增至地面。
- 测试步骤:
- 初始状态,恐龙Y=300(DINO_GROUND_Y),is_jump=false;
- 按下空格键,检查is_jump是否变为true,jump_h=100;
- 执行update(),观察Y是否每次减5,直到jump_h=0;
- 上升结束后,观察Y是否逐步加5,回到300,is_jump=false;
功能点2:绘制逻辑
- 设计:使用EGE的bar函数绘制矩形(x,y,x+宽,y+高)表示恐龙。
- 测试步骤:
- 初始化后,恐龙是否在X=50、Y=300位置绘制50×50矩形;
- 跳跃过程中,矩形位置是否随Y值变化而移动。
cpp
// ============ 恐龙类 ============
struct Dino {
int x = DINO_X; // 当前 X 坐标
int y = DINO_GROUND_Y; // 当前 Y 坐标
int jump_h = 0; // 剩余跳跃高度
bool is_jump = false; // 是否正在跳跃
/**
* 执行跳跃
* 只有在地面时才能跳跃
*/
void jump() {
if (!is_jump) {
is_jump = true;
jump_h = JUMP_HEIGHT;
}
}
/**
* 更新恐龙位置
* 处理跳跃上升和下落逻辑
*/
void update() {
if (is_jump) {
// 跳跃上升阶段
y -= JUMP_SPEED;
jump_h -= JUMP_SPEED;
if (jump_h <= 0) {
is_jump = false;
}
} else {
// 下落阶段
if (y < DINO_GROUND_Y) {
y += JUMP_SPEED;
}
}
}
/**
* 绘制恐龙
* 使用填充矩形表示
*/
void draw() const {
bar(x, y, x + DINO_WIDTH, y + DINO_HEIGHT);
}
};
测试恐龙模块
cpp
#include <graphics.h>
#include <stdlib.h>
#include <time.h>
#include <cstdio>
// ============ 你的原始常量定义 ============
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 400;
const int GROUND_Y = 350;
const int DINO_WIDTH = 50;
const int DINO_HEIGHT = 50;
const int DINO_X = 50;
const int DINO_GROUND_Y = 300;
const int JUMP_HEIGHT = 100;
const int JUMP_SPEED = 5;
const int FRAME_DELAY = 16;
// ============ 你的原始恐龙结构体 ============
struct Dino {
int x = DINO_X;
int y = DINO_GROUND_Y;
int jump_h = 0;
bool is_jump = false;
void jump() {
if (!is_jump) {
is_jump = true;
jump_h = JUMP_HEIGHT;
}
}
void update() {
if (is_jump) {
y -= JUMP_SPEED;
jump_h -= JUMP_SPEED;
if (jump_h <= 0) {
is_jump = false;
}
} else {
if (y < DINO_GROUND_Y) {
y += JUMP_SPEED;
}
}
}
void draw() const {
bar(x, y, x + DINO_WIDTH, y + DINO_HEIGHT);
}
};
// ============ 极简测试逻辑 ============
int main() {
// 1. 初始化EGE窗口
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE);
cleardevice();
srand(time(0));
// 定义宽字符数组存储格式化后的字符串(关键修复)
wchar_t coord_text[50]; // 足够存储"当前恐龙Y坐标:XXX"
// 2. 创建恐龙对象(仅测试恐龙功能)
Dino dino;
// 3. 测试循环:仅处理跳跃+更新+绘制
while (true) {
// 按键检测:仅空格触发跳跃
if (kbhit()) {
char key = getch();
if (key == ' ') {
dino.jump(); // 触发跳跃
}
// 按ESC退出测试
if (key == 27) break;
}
// 更新恐龙位置
dino.update();
// 绘制:仅地面+恐龙+测试提示
cleardevice();
// 绘制地面
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
// 设置中文字体 (使用宽字符)
/*
L 是宽字符(Wide Character)的前缀,表示使用 wchar_t 类型(Unicode 编码):
普通字符串 "中文" - 使用 char 类型,1字节/字符(GBK/ASCII编码)
宽字符串 L"中文" - 使用 wchar_t 类型,2字节/字符(UTF-16编码)
EGE 库支持中文时需要使用宽字符,因为 Windows API 内部使用 Unicode 处理字体。
*/
setfont(20, 0, L"微软雅黑");
setbkmode(TRANSPARENT); // 透明模型,不遮挡后面的图形
// 绘制测试提示
outtextxy(10, 20, L"恐龙跳跃测试(仅核心功能)");
outtextxy(10, 40, L"空格:跳跃 | ESC:退出");
// 显示坐标便于验证
// 使用 swprintf 格式化宽字符串
swprintf(coord_text, L"当前恐龙Y坐标:%d", dino.y);
outtextxy(10, 60, coord_text);
// 绘制恐龙
dino.draw();
// 帧率控制
delay_ms(FRAME_DELAY);
}
// 清理资源
closegraph();
return 0;
}

3. 障碍物模块(Obstacle结构体)
功能点1:随机速度与重置
- 设计:reset()方法将X重置为800(OBSTACLE_START_X),速度随机为6-9(6+rand()%4);update()方法X递减速度值,超出左侧屏幕(x < -30)返回true。
- 测试步骤:
- 初始状态,障碍物X=800,速度默认8;
- 执行update(),观察X是否每次减速度值;
- 当X=-31时,验证update()返回true;
- 调用reset(),检查X是否回到800,速度是否在6-9之间(多次调用验证随机性)。
功能点2:绘制逻辑
- 设计:使用bar函数绘制30×30矩形,位置随X/Y变化。
- 测试步骤:
- 初始状态,障碍物是否在X=800、Y=320位置绘制矩形;
- 移动过程中,矩形位置是否随X递减向左移动。
cpp
// ============ 障碍物类 ============
struct Obstacle {
int x = OBSTACLE_START_X; // 当前 X 坐标
int y = OBSTACLE_Y; // Y 坐标
int speed = 8; // 移动速度
/**
* 重置障碍物位置和速度
* 当障碍物超出屏幕左侧时调用
*/
void reset() {
x = OBSTACLE_START_X;
speed = 6 + rand() % 4; // 随机速度 6-9
}
/**
* 更新障碍物位置
* @return true 如果障碍物需要重置(超出屏幕),false 否则
*/
bool update() {
x -= speed;
if (x < -OBSTACLE_WIDTH) {
return true; // 返回 true 表示需要重置
}
return false;
}
/**
* 绘制障碍物
* 使用填充矩形表示
*/
void draw() const {
bar(x, y, x + OBSTACLE_WIDTH, y + OBSTACLE_HEIGHT);
}
};
障碍物模块测试
cpp
/**
* 恐龙跳跃游戏 - 障碍物模块专属测试
* 依赖:EGE图形库
* 测试点:障碍物初始状态、移动、重置、速度随机、绘制
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 随机数函数
#include <time.h> // 时间函数
#include <cstdio> // 格式化输出
// ============ 你的原始常量定义 ============
const int WINDOW_WIDTH = 800; // 窗口宽度
const int WINDOW_HEIGHT = 400; // 窗口高度
const int GROUND_Y = 350; // 地面 Y 坐标
const int OBSTACLE_WIDTH = 30; // 障碍物宽度
const int OBSTACLE_HEIGHT = 30; // 障碍物高度
const int OBSTACLE_Y = 320; // 障碍物 Y 坐标
const int OBSTACLE_START_X = 800; // 障碍物起始 X 坐标
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
// ============ 你的原始障碍物类 ============
struct Obstacle {
int x = OBSTACLE_START_X; // 当前 X 坐标
int y = OBSTACLE_Y; // Y 坐标
int speed = 8; // 移动速度
/**
* 重置障碍物位置和速度
* 当障碍物超出屏幕左侧时调用
*/
void reset() {
x = OBSTACLE_START_X;
speed = 6 + rand() % 4; // 随机速度 6-9
}
/**
* 更新障碍物位置
* @return true 如果障碍物需要重置(超出屏幕),false 否则
*/
bool update() {
x -= speed;
if (x < -OBSTACLE_WIDTH) {
return true; // 返回 true 表示需要重置
}
return false;
}
/**
* 绘制障碍物
* 使用填充矩形表示
*/
void draw() const {
bar(x, y, x + OBSTACLE_WIDTH, y + OBSTACLE_HEIGHT);
}
};
// ============ 障碍物专属测试逻辑 ============
int main() {
// 1. 初始化EGE窗口
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE); // 背景白色
cleardevice(); // 清空屏幕
srand(time(0)); // 初始化随机数种子(保证速度随机)
// 2. 创建障碍物对象(仅测试障碍物)
Obstacle obs;
setfont(18, 0, L"宋体");
setbkmode(TRANSPARENT);
// 3. 测试循环:仅处理障碍物更新+绘制+状态显示
while (true) {
// 退出逻辑:按ESC键退出测试
if (kbhit()) {
char key = getch();
if (key == 27) break; // ESC键退出
}
// 更新障碍物位置,检测是否需要重置
bool need_reset = obs.update();
if (need_reset) {
obs.reset(); // 超出屏幕则重置
}
// 绘制环节:仅保留地面+障碍物+测试信息
cleardevice();
// 绘制地面线(参考)
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
// 绘制测试提示和障碍物状态
wchar_t info[100];
swprintf(info, L"障碍物测试 | ESC退出");
outtextxy(10, 20, info);
swprintf(info, L"当前X坐标:%d | 速度:%d", obs.x, obs.speed);
outtextxy(10, 40, info);
swprintf(info, L"初始X:800 | 重置条件:X < -30");
outtextxy(10, 60, info);
// 绘制障碍物(红色填充,便于观察)
setfillcolor(RED);
obs.draw();
setfillcolor(WHITE); // 重置填充色
// 帧率控制(60FPS)
delay_ms(FRAME_DELAY);
}
// 清理资源
closegraph();
return 0;
}

4. 游戏状态模块(GameState结构体)
功能点1:分数累计
- 设计:障碍物重置时调用updateScore(),分数+1;reset()可重置分数为0、游戏结束状态为false。
- 测试步骤:
- 初始分数为0,游戏结束状态为false;
- 障碍物第一次重置后,检查分数是否变为1;
- 多次重置后,分数是否逐次递增;
- 调用reset(),验证分数回到0,game_over=false。
功能点2:游戏结束判定
- 设计:碰撞检测触发时,game_over设为true,退出游戏循环。
- 测试步骤:
- 强制让恐龙与障碍物坐标重叠(如修改恐龙X=800);
- 执行碰撞检测后,验证game_over=true;
- 游戏循环退出,显示"Game Over"。
cpp
// ============ 游戏状态类 ============
struct GameState {
int score = 0; // 当前分数
bool game_over = false; // 游戏是否结束
/**
* 重置游戏状态
* 用于重新开始游戏
*/
void reset() {
score = 0;
game_over = false;
}
/**
* 增加分数
*/
void updateScore() {
score++;
}
};
游戏状态模块测试
cpp
/**
* 恐龙跳跃游戏 - 游戏状态类专属测试
* 依赖:EGE图形库
* 测试点:初始状态、分数累加、状态重置、游戏结束标记
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 基础工具函数
#include <time.h> // 时间函数(仅初始化随机数,无实际作用)
#include <cstdio> // 格式化输出
// ============ 你的原始常量定义(仅保留显示相关) ============
const int WINDOW_WIDTH = 800; // 窗口宽度
const int WINDOW_HEIGHT = 400; // 窗口高度
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
// ============ 你的原始游戏状态类 ============
struct GameState {
int score = 0; // 当前分数
bool game_over = false; // 游戏是否结束
/**
* 重置游戏状态
* 用于重新开始游戏
*/
void reset() {
score = 0;
game_over = false;
}
/**
* 增加分数
*/
void updateScore() {
score++;
}
};
// ============ 游戏状态专属测试逻辑 ============
int main() {
// 1. 初始化EGE窗口(仅用于显示测试信息)
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE); // 背景白色
cleardevice(); // 清空屏幕
srand(time(0)); // 初始化随机数(无实际作用,仅保留原代码结构)
// 2. 创建游戏状态对象(仅测试该类)
GameState game_state;
// 字体与文字遮挡
setfont(16, 0, L"宋体");
setbkmode(TRANSPARENT);
setcolor(BLACK);
// 3. 测试循环:交互控制+状态显示
while (true) {
// 按键交互逻辑(仅测试游戏状态操作)
if (kbhit()) {
char key = getch();
switch (key) {
case '=': // 按等号键:增加分数(模拟躲避障碍物加分)
game_state.updateScore();
break;
case 'o': // 按O键:标记游戏结束(模拟碰撞触发结束)
game_state.game_over = true;
break;
case 'r': // 按R键:重置游戏状态(模拟重启游戏)
game_state.reset();
break;
case 27: // 按ESC键:退出测试
closegraph();
return 0;
default:
break;
}
}
// 绘制测试信息(直观展示游戏状态)
cleardevice();
setcolor(BLACK); // 文字黑色
// 操作提示
outtextxy(10, 20, L"游戏状态类测试 | 操作说明:");
outtextxy(10, 40, L"= 键:分数+1 | o 键:标记游戏结束 | r 键:重置状态 | ESC:退出");
// 当前状态显示
wchar_t state_info[100];
swprintf(state_info, L"当前分数:%d", game_state.score);
outtextxy(10, 70, state_info);
swprintf(state_info, L"游戏结束状态:%ls", game_state.game_over ? L"已结束" : L"未结束");
outtextxy(10, 90, state_info);
// 帧率控制(保持原代码的60FPS逻辑)
delay_ms(FRAME_DELAY);
}
// 清理资源(兜底)
closegraph();
return 0;
}

5. 绘制模块
功能点1:背景与分数绘制
- 设计:drawBackground()绘制地面线(0,350→800,350);drawScore()通过sprintf格式化字符串,在右上角显示分数。
- 测试步骤:
- 运行程序,验证窗口底部有一条地面线;
- 分数为0时,右上角显示"Score: 0",分数增加后文字同步更新。
功能点2:游戏结束文字绘制
- 设计:drawGameOver()在窗口中间显示"Game Over!"。
- 测试步骤:
- 游戏结束后,验证文字在(400-50,200)位置显示,即窗口居中。
cpp
// ============ 绘制函数 ============
/**
* 绘制游戏背景
* 只绘制地面线
*/
void drawBackground() {
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
}
/**
* 绘制当前分数
* @param score 要显示的分数值
*/
void drawScore(int score) {
char score_text[20];
sprintf(score_text, "Score: %d", score);
outtextxy(WINDOW_WIDTH - 100, 20, score_text);
}
/**
* 绘制游戏结束信息
*/
void drawGameOver() {
outtextxy(WINDOW_WIDTH / 2 - 50, WINDOW_HEIGHT / 2, "Game Over!");
}
绘制模块测试
cpp
/**
* 恐龙跳跃游戏 - 绘制函数专属测试
* 依赖:EGE图形库
* 测试点:背景(地面线)、分数、游戏结束文字的绘制位置与效果
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 基础工具函数
#include <time.h> // 时间函数(仅初始化,无实际作用)
#include <cstdio> // 格式化输出
// ============ 你的原始常量定义(补全缺失的WINDOW_WIDTH) ============
const int WINDOW_WIDTH = 800; // 补全你注释掉的窗口宽度(绘制函数依赖)
const int WINDOW_HEIGHT = 400; // 窗口高度
const int GROUND_Y = 350; // 地面 Y 坐标
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
// ============ 你的原始绘制函数 ============
/**
* 绘制游戏背景
* 只绘制地面线
*/
void drawBackground() {
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
}
/**
* 绘制当前分数
* @param score 要显示的分数值
*/
void drawScore(int score) {
char score_text[20];
sprintf(score_text, "Score: %d", score);
outtextxy(WINDOW_WIDTH - 100, 20, score_text);
}
/**
* 绘制游戏结束信息
*/
void drawGameOver() {
outtextxy(WINDOW_WIDTH / 2 - 50, WINDOW_HEIGHT / 2, "Game Over!");
}
// ============ 绘制函数专属测试逻辑 ============
int main() {
// 1. 初始化EGE窗口(绘制函数依赖窗口环境)
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE); // 背景白色,便于观察绘制内容
cleardevice(); // 清空初始屏幕
setcolor(BLACK); // 绘制颜色设为黑色(默认)
srand(time(0)); // 保留原代码结构,无实际作用
// 测试用变量
int test_score = 0; // 测试分数(可通过按键修改)
bool show_game_over = false; // 是否显示游戏结束文字
// 2. 测试循环:交互控制+绘制函数调用
while (true) {
// 按键交互逻辑(控制测试状态)
if (kbhit()) {
char key = getch();
switch (key) {
case '=': // 按等号键:分数+1(测试drawScore更新)
test_score++;
break;
case 'o': // 按O键:显示/隐藏游戏结束文字(测试drawGameOver)
show_game_over = !show_game_over;
break;
case 'r': // 按R键:重置分数和结束文字
test_score = 0;
show_game_over = false;
break;
case 27: // 按ESC键:退出测试
closegraph();
return 0;
default:
break;
}
}
// 核心绘制流程(按原游戏逻辑顺序调用绘制函数)
cleardevice(); // 清空上一帧内容
drawBackground(); // 绘制地面线(基础背景)
// 绘制分数(始终显示)
drawScore(test_score);
// 按需绘制游戏结束文字
if (show_game_over) {
drawGameOver();
}
// 绘制测试提示(辅助验证)
outtextxy(10, 20, L"绘制函数测试 | 操作说明:");
outtextxy(10, 40, L"= 键:分数+1 | o 键:显示/隐藏GameOver | r 键:重置 | ESC:退出");
outtextxy(10, 60, L"验证点:地面线(Y=350)、分数(右上角)、GameOver(居中)");
// 帧率控制(保持原代码的60FPS逻辑)
delay_ms(FRAME_DELAY);
}
// 清理资源(兜底)
closegraph();
return 0;
}

6. 输入处理模块(handleInput函数)
功能点:按键检测
- 设计:检测kbhit()(按键事件),getch()获取按键,空格键(' ')或上方向键(72)触发恐龙跳跃。
- 测试步骤:
- 未按键时,恐龙不跳跃;
- 按下空格键,触发跳跃;
- 按上方向键,触发跳跃;
- 按其他键(如A、Enter),不触发跳跃。
cpp
/**
* 处理用户输入
* 检测空格键或上方向键,触发恐龙跳跃
* @param dino 恐龙对象的引用
*/
void handleInput(Dino& dino) {
if (kbhit()) {
char key = getch();
if (key == ' ' || key == 72) { // 空格键或上方向键
dino.jump();
}
}
}
输入处理模块测试
cpp
/**
* 恐龙跳跃游戏 - 输入处理函数专属测试
* 依赖:EGE图形库
* 测试点:空格键/上方向键检测、jump()调用触发、非目标按键过滤
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 基础工具函数
#include <time.h> // 时间函数(仅初始化)
#include <cstdio> // 格式化输出
// ============ 补全缺失的常量(输入函数依赖) ============
const int WINDOW_WIDTH = 800; // 补全你注释掉的窗口宽度
const int WINDOW_HEIGHT = 400; // 窗口高度
const int GROUND_Y = 350; // 地面 Y 坐标
const int DINO_WIDTH = 50; // 恐龙宽度
const int DINO_HEIGHT = 50; // 恐龙高度
const int DINO_X = 50; // 恐龙初始 X 坐标
const int DINO_GROUND_Y = 300; // 恐龙地面 Y 坐标
const int JUMP_HEIGHT = 100; // 跳跃高度
const int JUMP_SPEED = 5; // 跳跃/下落速度
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
// ============ 你的原始恐龙类(输入函数依赖,必须保留) ============
struct Dino {
int x = DINO_X; // 当前 X 坐标
int y = DINO_GROUND_Y; // 当前 Y 坐标
int jump_h = 0; // 剩余跳跃高度
bool is_jump = false; // 是否正在跳跃
/**
* 执行跳跃
* 在地面和空中时才能跳跃
*/
void jump() {
if (!is_jump) {
is_jump = true;
jump_h = JUMP_HEIGHT;
}
}
/**
* 更新恐龙位置
* 处理跳跃上升和下落逻辑
*/
void update() {
if (is_jump) {
// 跳跃上升阶段
y -= JUMP_SPEED;
jump_h -= JUMP_SPEED;
if (jump_h <= 0) {
is_jump = false;
}
} else {
// 下落阶段
if (y < DINO_GROUND_Y) {
y += JUMP_SPEED;
}
}
}
/**
* 绘制恐龙
* 使用填充矩形表示
*/
void draw() const {
bar(x, y, x + DINO_WIDTH, y + DINO_HEIGHT);
}
};
// ============ 你的原始输入处理函数 ============
/**
* 处理用户输入
* 检测空格键或上方向键,触发恐龙跳跃
* @param dino 恐龙对象的引用
*/
void handleInput(Dino& dino) {
if (kbhit()) {
char key = getch();
if (key == ' ' || key == 72) { // 空格键或上方向键
dino.jump();
}
}
}
// ============ 辅助绘制函数(仅用于可视化测试) ============
void drawBackground() {
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
}
// ============ 输入处理专属测试逻辑 ============
int main() {
// 1. 初始化EGE窗口(输入+绘制依赖窗口环境)
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE); // 背景白色,便于观察
cleardevice(); // 清空初始屏幕
setcolor(BLACK); // 绘制颜色设为黑色
srand(time(0)); // 保留原代码结构
// 2. 创建测试对象
Dino dino; // 输入函数的入参依赖
int key_trigger_count = 0; // 记录按键触发次数(辅助验证)
// 3. 测试循环:输入处理+状态显示+可视化验证
while (true) {
// 核心:调用你的输入处理函数
handleInput(dino);
// 检测是否触发了jump(辅助统计,验证输入函数效果)
static bool last_is_jump = false;
if (!last_is_jump && dino.is_jump) {
key_trigger_count++; // 每次触发跳跃,计数+1
}
last_is_jump = dino.is_jump;
// 更新恐龙位置(让跳跃效果可视化)
dino.update();
// 绘制测试界面(直观验证输入效果)
cleardevice();
drawBackground(); // 绘制地面线
dino.draw(); // 绘制恐龙(跳跃效果直观可见)
// 显示测试信息
char info[100];
outtextxy(10, 20, "输入处理函数测试 | 操作说明:");
outtextxy(10, 40, "空格键/上方向键:触发跳跃 | ESC:退出");
outtextxy(10, 60, "验证点:仅指定按键触发跳跃,其他按键无反应");
sprintf(info, "跳跃触发次数:%d | 恐龙Y坐标:%d", key_trigger_count, dino.y);
outtextxy(10, 80, info);
sprintf(info, "当前跳跃状态:%s", dino.is_jump ? "跳跃中" : "未跳跃");
outtextxy(10, 100, info);
// 退出逻辑
if (kbhit()) {
char key = getch();
if (key == 27) { // ESC键退出
closegraph();
return 0;
}
// 验证非目标按键:按A/S/D等键,触发次数不增加
}
// 帧率控制
delay_ms(FRAME_DELAY);
}
// 清理资源
closegraph();
return 0;
}

7. 碰撞检测模块(checkCollision函数)
功能点:AABB碰撞检测
- 设计:判断恐龙与障碍物的矩形边界是否重叠,条件:
dino.x + 50 > obs.x && dino.x < obs.x + 30 && dino.y + 50 > obs.y - 测试步骤:
- 恐龙X=50、Y=300,障碍物X=60、Y=320 → 满足条件,返回true(碰撞);
- 恐龙X=50、Y=200(空中),障碍物X=60、Y=320 → dino.y+50=250 < 320,返回false(不碰撞);
- 恐龙X=50、Y=300,障碍物X=110、Y=320 → dino.x+50=100 < 110,返回false(不碰撞)。
cpp
// ============ 碰撞检测 ============
/**
* 检测恐龙和障碍物是否碰撞
* 使用 AABB(轴对齐边界框)碰撞检测算法
* @param dino 恐龙对象
* @param obs 障碍物对象
* @return true 如果发生碰撞,false 否则
*/
bool checkCollision(const Dino& dino, const Obstacle& obs) {
return dino.x + DINO_WIDTH > obs.x &&
dino.x < obs.x + OBSTACLE_WIDTH &&
dino.y + DINO_HEIGHT > obs.y;
}
碰撞检测与单次游戏功能检测
cpp
/**
* 恐龙跳跃游戏 - 碰撞检测+单次游戏流程测试
* 依赖:EGE图形库
* 测试点:碰撞检测准确性、单次游戏完整流程(跳跃/移动/计分/结束)
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 随机数函数
#include <time.h> // 时间函数
#include <cstdio> // 格式化输出
// ============ 你的原始常量定义 ============
const int WINDOW_WIDTH = 800; // 窗口宽度
const int WINDOW_HEIGHT = 400; // 窗口高度
const int GROUND_Y = 350; // 地面 Y 坐标
const int DINO_WIDTH = 50; // 恐龙宽度
const int DINO_HEIGHT = 50; // 恐龙高度
const int DINO_X = 50; // 恐龙初始 X 坐标
const int DINO_GROUND_Y = 300; // 恐龙地面 Y 坐标
const int JUMP_HEIGHT = 100; // 跳跃高度
const int JUMP_SPEED = 5; // 跳跃/下落速度
const int OBSTACLE_WIDTH = 30; // 障碍物宽度
const int OBSTACLE_HEIGHT = 30; // 障碍物高度
const int OBSTACLE_Y = 320; // 障碍物 Y 坐标
const int OBSTACLE_START_X = 800; // 障碍物起始 X 坐标
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
// ============ 你的原始恐龙类 ============
struct Dino {
int x = DINO_X; // 当前 X 坐标
int y = DINO_GROUND_Y; // 当前 Y 坐标
int jump_h = 0; // 剩余跳跃高度
bool is_jump = false; // 是否正在跳跃
void jump() {
if (!is_jump) {
is_jump = true;
jump_h = JUMP_HEIGHT;
}
}
void update() {
if (is_jump) {
y -= JUMP_SPEED;
jump_h -= JUMP_SPEED;
if (jump_h <= 0) {
is_jump = false;
}
} else {
if (y < DINO_GROUND_Y) {
y += JUMP_SPEED;
}
}
}
void draw() const {
setfillcolor(BLACK);
bar(x, y, x + DINO_WIDTH, y + DINO_HEIGHT);
setfillcolor(WHITE);
}
};
// ============ 你的原始障碍物类 ============
struct Obstacle {
int x = OBSTACLE_START_X; // 当前 X 坐标
int y = OBSTACLE_Y; // Y 坐标
int speed = 8; // 移动速度
void reset() {
x = OBSTACLE_START_X;
speed = 6 + rand() % 4; // 随机速度 6-9
}
bool update() {
x -= speed;
if (x < -OBSTACLE_WIDTH) {
return true;
}
return false;
}
void draw() const {
setfillcolor(RED);
bar(x, y, x + OBSTACLE_WIDTH, y + OBSTACLE_HEIGHT);
setfillcolor(WHITE);
}
};
// ============ 你的原始游戏状态类 ============
struct GameState {
int score = 0; // 当前分数
bool game_over = false; // 游戏是否结束
void reset() {
score = 0;
game_over = false;
}
void updateScore() {
score++;
}
};
// ============ 你的原始绘制函数 ============
void drawBackground() {
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
}
void drawScore(int score) {
char score_text[20];
sprintf(score_text, "Score: %d", score);
outtextxy(WINDOW_WIDTH - 100, 20, score_text);
}
void drawGameOver() {
setcolor(RED);
outtextxy(WINDOW_WIDTH / 2 - 50, WINDOW_HEIGHT / 2, "Game Over!");
setcolor(BLACK);
}
// ============ 你的原始输入处理函数 ============
void handleInput(Dino& dino) {
if (kbhit()) {
char key = getch();
if (key == ' ' || key == 72) {
dino.jump();
}
}
}
// ============ 你的原始碰撞检测函数 ============
bool checkCollision(const Dino& dino, const Obstacle& obs) {
return dino.x + DINO_WIDTH > obs.x &&
dino.x < obs.x + OBSTACLE_WIDTH &&
dino.y + DINO_HEIGHT > obs.y;
}
// ============ 单次游戏流程测试逻辑 ============
int main() {
// 1. 初始化EGE环境
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE);
cleardevice();
setcolor(BLACK);
srand(time(0)); // 初始化随机数(障碍物速度)
// 2. 创建游戏核心对象
Dino dino;
Obstacle obs;
GameState game_state;
// 3. 单次游戏主循环(游戏结束即终止)
while (!game_state.game_over) {
// 3.1 输入处理
handleInput(dino);
// 3.2 更新游戏对象
dino.update();
bool need_reset_obs = obs.update();
// 3.3 障碍物重置+分数累加
if (need_reset_obs) {
obs.reset();
game_state.updateScore();
}
// 3.4 碰撞检测(核心测试点)
if (checkCollision(dino, obs)) {
game_state.game_over = true; // 碰撞触发游戏结束
}
// 3.5 绘制所有元素
cleardevice();
drawBackground();
dino.draw();
obs.draw();
drawScore(game_state.score);
// 3.6 帧率控制
delay_ms(FRAME_DELAY);
}
// 4. 游戏结束阶段:显示结束画面
cleardevice();
drawBackground();
drawScore(game_state.score); // 显示最终分数
drawGameOver();
outtextxy(10, 20, "碰撞检测触发游戏结束 | 按ESC退出");
// 5. 等待退出
while (true) {
if (kbhit() && getch() == 27) { // ESC键退出
break;
}
delay_ms(FRAME_DELAY);
}
// 6. 清理资源
closegraph();
return 0;
}

8. 游戏循环模块(gameLoop函数)
功能点:循环逻辑整合
- 设计:循环直到game_over=true,依次执行输入→更新→重置检测→绘制→碰撞检测→帧率控制。
- 测试步骤:
- 运行程序,验证循环持续执行,帧率稳定(约60FPS);
- 障碍物重置时,分数同步增加;
- 碰撞发生后,循环退出,显示Game Over;
- 帧延迟16ms,验证游戏无过快/过慢运行。
9. 初始化与清理模块
功能点1:窗口初始化
- 设计:initGame()调用initgraph创建窗口,设置白色背景,初始化随机数种子(srand(time(0)))。
- 测试步骤:
- 运行程序,窗口正常弹出,背景为白色;
- 多次运行,障碍物速度随机(6-9),验证随机数种子生效。
功能点2:资源清理
- 设计:cleanupGame()等待按键后关闭窗口(closegraph())。
- 测试步骤:
- 游戏结束后,按下任意键,窗口关闭;
- 无内存泄漏(EGE库基础封装,无需额外内存管理)。
cpp
// ============ 游戏循环 ============
/**
* 游戏主循环
* 处理输入、更新状态、绘制画面、检测碰撞
* @param dino 恐龙对象引用
* @param obs 障碍物对象引用
* @param game_state 游戏状态对象引用
*/
void gameLoop(Dino& dino, Obstacle& obs, GameState& game_state) {
while (!game_state.game_over) {
// 1. 处理用户输入
handleInput(dino);
// 2. 更新游戏对象状态
dino.update();
bool needs_reset = obs.update();
// 3. 检查障碍物是否超出屏幕并更新分数
if (needs_reset) {
obs.reset();
game_state.updateScore();
}
// 4. 绘制游戏画面
cleardevice();
drawBackground();
dino.draw();
obs.draw();
drawScore(game_state.score);
// 5. 碰撞检测
if (checkCollision(dino, obs)) {
game_state.game_over = true;
}
// 6. 控制帧率
delay_ms(FRAME_DELAY);
}
}
// ============ 初始化与清理 ============
/**
* 初始化游戏
* 创建图形窗口并设置初始状态
*/
void initGame() {
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE);
cleardevice();
srand(time(0)); // 初始化随机数种子
}
/**
* 清理游戏资源
* 等待用户按键后关闭图形窗口
*/
void cleanupGame() {
getch();
closegraph();
}
// ============ 主函数 ============
/**
* 程序入口
* 初始化游戏 -> 运行游戏循环 -> 显示结果 -> 清理资源
*/
int main() {
// 初始化游戏
initGame();
// 创建游戏对象
Dino dino;
Obstacle obs;
GameState game_state;
// 运行游戏循环
gameLoop(dino, obs, game_state);
// 显示游戏结束信息
drawGameOver();
// 清理资源并退出
cleanupGame();
return 0;
}
完成测试代码如下:
cpp
/**
* 恐龙跳跃游戏
* 简单的 2D 跑酷游戏,玩家控制恐龙跳跃躲避障碍物
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 随机数函数
#include <time.h> // 时间函数
#include <cstdio> // 格式化输出
// ============ 常量定义 ============
const int WINDOW_WIDTH = 800; // 窗口宽度
const int WINDOW_HEIGHT = 400; // 窗口高度
const int GROUND_Y = 350; // 地面 Y 坐标
const int DINO_WIDTH = 50; // 恐龙宽度
const int DINO_HEIGHT = 50; // 恐龙高度
const int DINO_X = 50; // 恐龙初始 X 坐标
const int DINO_GROUND_Y = 300; // 恐龙地面 Y 坐标
const int JUMP_HEIGHT = 100; // 跳跃高度
const int JUMP_SPEED = 5; // 跳跃/下落速度
const int OBSTACLE_WIDTH = 30; // 障碍物宽度
const int OBSTACLE_HEIGHT = 30; // 障碍物高度
const int OBSTACLE_Y = 320; // 障碍物 Y 坐标
const int OBSTACLE_START_X = 800; // 障碍物起始 X 坐标
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
// ============ 恐龙类 ============
struct Dino {
int x = DINO_X; // 当前 X 坐标
int y = DINO_GROUND_Y; // 当前 Y 坐标
int jump_h = 0; // 剩余跳跃高度
bool is_jump = false; // 是否正在跳跃
/**
* 执行跳跃
* 在地面和空中时才能跳跃
*/
void jump() {
if (!is_jump) {
is_jump = true;
jump_h = JUMP_HEIGHT;
}
}
/**
* 更新恐龙位置
* 处理跳跃上升和下落逻辑
*/
void update() {
if (is_jump) {
// 跳跃上升阶段
y -= JUMP_SPEED;
jump_h -= JUMP_SPEED;
if (jump_h <= 0) {
is_jump = false;
}
} else {
// 下落阶段
if (y < DINO_GROUND_Y) {
y += JUMP_SPEED;
}
}
}
/**
* 绘制恐龙
* 使用填充矩形表示
*/
void draw() const {
bar(x, y, x + DINO_WIDTH, y + DINO_HEIGHT);
}
};
// ============ 障碍物类 ============
struct Obstacle {
int x = OBSTACLE_START_X; // 当前 X 坐标
int y = OBSTACLE_Y; // Y 坐标
int speed = 8; // 移动速度
/**
* 重置障碍物位置和速度
* 当障碍物超出屏幕左侧时调用
*/
void reset() {
x = OBSTACLE_START_X;
speed = 6 + rand() % 4; // 随机速度 6-9
}
/**
* 更新障碍物位置
* @return true 如果障碍物需要重置(超出屏幕),false 否则
*/
bool update() {
x -= speed;
if (x < -OBSTACLE_WIDTH) {
return true; // 返回 true 表示需要重置
}
return false;
}
/**
* 绘制障碍物
* 使用填充矩形表示
*/
void draw() const {
bar(x, y, x + OBSTACLE_WIDTH, y + OBSTACLE_HEIGHT);
}
};
// ============ 游戏状态类 ============
struct GameState {
int score = 0; // 当前分数
bool game_over = false; // 游戏是否结束
/**
* 重置游戏状态
* 用于重新开始游戏
*/
void reset() {
score = 0;
game_over = false;
}
/**
* 增加分数
*/
void updateScore() {
score++;
}
};
// ============ 绘制函数 ============
/**
* 绘制游戏背景
* 只绘制地面线
*/
void drawBackground() {
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
}
/**
* 绘制当前分数
* @param score 要显示的分数值
*/
void drawScore(int score) {
char score_text[20];
sprintf(score_text, "Score: %d", score);
outtextxy(WINDOW_WIDTH - 100, 20, score_text);
}
/**
* 绘制游戏结束信息
*/
void drawGameOver() {
outtextxy(WINDOW_WIDTH / 2 - 50, WINDOW_HEIGHT / 2, "Game Over!");
}
// ============ 输入处理 ============
/**
* 处理用户输入
* 检测空格键或上方向键,触发恐龙跳跃
* @param dino 恐龙对象的引用
*/
void handleInput(Dino& dino) {
if (kbhit()) {
char key = getch();
if (key == ' ' || key == 72) { // 空格键或上方向键
dino.jump();
}
}
}
// ============ 碰撞检测 ============
/**
* 检测恐龙和障碍物是否碰撞
* 使用 AABB(轴对齐边界框)碰撞检测算法
* @param dino 恐龙对象
* @param obs 障碍物对象
* @return true 如果发生碰撞,false 否则
*/
bool checkCollision(const Dino& dino, const Obstacle& obs) {
return dino.x + DINO_WIDTH > obs.x &&
dino.x < obs.x + OBSTACLE_WIDTH &&
dino.y + DINO_HEIGHT > obs.y;
}
// ============ 游戏循环 ============
/**
* 游戏主循环
* 处理输入、更新状态、绘制画面、检测碰撞
* @param dino 恐龙对象引用
* @param obs 障碍物对象引用
* @param game_state 游戏状态对象引用
*/
void gameLoop(Dino& dino, Obstacle& obs, GameState& game_state) {
while (!game_state.game_over) {
// 1. 处理用户输入
handleInput(dino);
// 2. 更新游戏对象状态
dino.update();
bool needs_reset = obs.update();
// 3. 检查障碍物是否超出屏幕并更新分数
if (needs_reset) {
obs.reset();
game_state.updateScore();
}
// 4. 绘制游戏画面
cleardevice();
drawBackground();
dino.draw();
obs.draw();
drawScore(game_state.score);
// 5. 碰撞检测
if (checkCollision(dino, obs)) {
game_state.game_over = true;
}
// 6. 控制帧率
delay_ms(FRAME_DELAY);
}
}
// ============ 初始化与清理 ============
/**
* 初始化游戏
* 创建图形窗口并设置初始状态
*/
void initGame() {
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE);
cleardevice();
srand(time(0)); // 初始化随机数种子
}
/**
* 清理游戏资源
* 等待用户按键后关闭图形窗口
*/
void cleanupGame() {
getch();
closegraph();
}
// ============ 主函数 ============
/**
* 程序入口
* 初始化游戏 -> 运行游戏循环 -> 显示结果 -> 清理资源
*/
int main() {
// 初始化游戏
initGame();
// 创建游戏对象
Dino dino;
Obstacle obs;
GameState game_state;
// 运行游戏循环
gameLoop(dino, obs, game_state);
// 显示游戏结束信息
drawGameOver();
// 清理资源并退出
cleanupGame();
return 0;
}
四、测试阶段(整体验证)
1. 单元测试(功能点全覆盖)
对上述每个功能点单独测试,确保单个模块逻辑正确(如跳跃、障碍物移动、分数累计、碰撞检测)。
2. 集成测试(模块联动)
- 流程测试:启动游戏→按空格跳跃→躲避障碍物→分数增加→碰撞后游戏结束→按键退出,全流程无异常;
- 边界测试:
- 恐龙跳跃到最高处时躲避障碍物,验证不碰撞;
- 障碍物速度为9(最快)时,恐龙仍可通过跳跃躲避;
- 分数累计到100+时,文字显示无溢出(右上角"Score: XXX"正常显示)。
3. 异常测试
- 连续快速按空格,验证无二次跳跃;
- 游戏结束后,按键无响应(仅等待退出);
- 窗口大小固定,拖拽窗口不影响游戏逻辑。
五、优化版本
cpp
/**
* 恐龙跳跃游戏
* 新增:F1键切换调试信息显示/隐藏,调试信息用醒目颜色展示全量状态
*/
#include <graphics.h> // EGE 图形库
#include <stdlib.h> // 随机数函数
#include <time.h> // 时间函数
#include <cstdio> // 格式化输出
#include <cwchar> // 宽字符处理(解决中文显示)
#include <locale.h> // 本地化设置(中文显示)
// ============ 常量定义 ============
const int WINDOW_WIDTH = 800; // 窗口宽度
const int WINDOW_HEIGHT = 400; // 窗口高度
const int GROUND_Y = 350; // 地面 Y 坐标
const int DINO_WIDTH = 50; // 恐龙宽度
const int DINO_HEIGHT = 50; // 恐龙高度
const int DINO_X = 50; // 恐龙初始 X 坐标
const int DINO_GROUND_Y = 300; // 恐龙地面 Y 坐标
const int JUMP_HEIGHT = 100; // 跳跃高度
const int JUMP_SPEED = 5; // 跳跃/下落速度
const int OBSTACLE_WIDTH = 30; // 障碍物宽度
const int OBSTACLE_HEIGHT = 30; // 障碍物高度
const int OBSTACLE_Y = 320; // 障碍物 Y 坐标
const int OBSTACLE_START_X = 800; // 障碍物起始 X 坐标
const int FRAME_DELAY = 16; // 帧延迟(约 60 FPS)
bool show_debug_info = true; // 调试信息显示开关(默认开启)
// ============ 恐龙类 ============
struct Dino {
int x = DINO_X; // 当前 X 坐标
int y = DINO_GROUND_Y; // 当前 Y 坐标
int jump_h = 0; // 剩余跳跃高度
bool is_jump = false; // 是否正在跳跃
/**
* 执行跳跃
* 在地面和空中时才能跳跃
*/
void jump() {
if (!is_jump) {
is_jump = true;
jump_h = JUMP_HEIGHT;
}
}
/**
* 更新恐龙位置
* 处理跳跃上升和下落逻辑
*/
void update() {
if (is_jump) {
// 跳跃上升阶段
y -= JUMP_SPEED;
jump_h -= JUMP_SPEED;
if (jump_h <= 0) {
is_jump = false;
}
} else {
// 下落阶段
if (y < DINO_GROUND_Y) {
y += JUMP_SPEED;
}
}
}
/**
* 绘制恐龙
* 使用填充矩形表示
*/
void draw() const {
setfillcolor(BLACK); // 恐龙黑色
bar(x, y, x + DINO_WIDTH, y + DINO_HEIGHT);
setfillcolor(WHITE);
}
};
// ============ 障碍物类 ============
struct Obstacle {
int x = OBSTACLE_START_X; // 当前 X 坐标
int y = OBSTACLE_Y; // Y 坐标
int speed = 8; // 移动速度
/**
* 重置障碍物位置和速度
* 当障碍物超出屏幕左侧时调用
*/
void reset() {
x = OBSTACLE_START_X;
speed = 6 + rand() % 4; // 随机速度 6-9
}
/**
* 更新障碍物位置
* @return true 如果障碍物需要重置(超出屏幕),false 否则
*/
bool update() {
x -= speed;
if (x < -OBSTACLE_WIDTH) {
return true; // 返回 true 表示需要重置
}
return false;
}
/**
* 绘制障碍物
* 使用填充矩形表示
*/
void draw() const {
setfillcolor(RED); // 障碍物红色
bar(x, y, x + OBSTACLE_WIDTH, y + OBSTACLE_HEIGHT);
setfillcolor(WHITE);
}
};
// ============ 游戏状态类 ============
struct GameState {
int score = 0; // 当前分数
bool game_over = false; // 游戏是否结束
/**
* 重置游戏状态
* 用于重新开始游戏
*/
void reset() {
score = 0;
game_over = false;
}
/**
* 增加分数
*/
void updateScore() {
score++;
}
};
// ============ 绘制函数 ============
/**
* 绘制游戏背景
* 只绘制地面线
*/
void drawBackground() {
setcolor(BLACK);
line(0, GROUND_Y, WINDOW_WIDTH, GROUND_Y);
}
/**
* 绘制当前分数
* @param score 要显示的分数值
*/
void drawScore(int score) {
setcolor(BLACK);
wchar_t score_text[20];
swprintf(score_text, 20, L"Score: %d", score);
outtextxy(WINDOW_WIDTH - 100, 20, score_text);
}
/**
* 绘制游戏结束信息
*/
void drawGameOver() {
setcolor(RED);
setfont(30, 0, L"微软雅黑");
outtextxy(WINDOW_WIDTH / 2 - 80, WINDOW_HEIGHT / 2, L"Game Over!");
setfont(20, 0, L"微软雅黑");
setcolor(BLACK);
}
void drawDebugInfo(const Dino& dino, const Obstacle& obs, const GameState& game_state) {
if (!show_debug_info) return;
// 设置调试信息字体和颜色(黄色背景+红色文字,醒目)
setfont(18, 0, L"微软雅黑");
setbkmode(OPAQUE); // 不透明背景
setfillcolor(YELLOW); // 背景黄色
setcolor(RED); // 文字红色
// 存储格式化后的调试信息
wchar_t debug_text[100];
int y_offset = 20; // 文本起始Y坐标
// 核心修改:中间偏右位置(窗口宽度-400,避开左侧恐龙区域)
int x_pos = WINDOW_WIDTH-600; // 文本起始X坐标
// 1. 恐龙状态
swprintf(debug_text, 100, L"【恐龙状态】 X: %d, Y: %d", dino.x, dino.y);
outtextxy(x_pos, y_offset, debug_text);
y_offset += 25;
swprintf(debug_text, 100, L" 跳跃状态: %ls, 剩余跳跃高度: %d",
dino.is_jump ? L"跳跃中" : L"未跳跃", dino.jump_h);
outtextxy(x_pos, y_offset, debug_text);
y_offset += 25;
// 2. 障碍物状态
swprintf(debug_text, 100, L"【障碍物状态】 X: %d, Y: %d", obs.x, obs.y);
outtextxy(x_pos, y_offset, debug_text);
y_offset += 25;
swprintf(debug_text, 100, L" 移动速度: %d, 重置阈值: X < -%d", obs.speed, OBSTACLE_WIDTH);
outtextxy(x_pos, y_offset, debug_text);
y_offset += 25;
// 3. 游戏状态
swprintf(debug_text, 100, L"【游戏状态】 分数: %d, 游戏结束: %ls",
game_state.score, game_state.game_over ? L"是" : L"否");
outtextxy(x_pos, y_offset, debug_text);
y_offset += 25;
// 4. 操作提示(核心修改:Tab键替代F1)
swprintf(debug_text, 100, L"【操作】 Tab: 隐藏/显示调试信息 | 空格/上方向键: 跳跃 | 碰撞结束游戏");
outtextxy(x_pos, y_offset, debug_text);
// 恢复默认绘制设置
setbkmode(TRANSPARENT);
setcolor(BLACK);
setfillcolor(WHITE);
}
// ============ 输入处理(核心修改:Tab键切换调试信息) ============
void handleInput(Dino& dino) {
if (kbhit()) {
int key = getch(); // 用int接收,兼容所有按键
// Tab键(ASCII=9)切换调试信息显示(核心修改)
if (key == 9) {
show_debug_info = !show_debug_info;
}
// 空格键(32)或上方向键(72)触发跳跃
else if (key == 32 || key == 72) {
dino.jump();
}
}
}
// ============ 碰撞检测 ============
/**
* 检测恐龙和障碍物是否碰撞
* 使用 AABB(轴对齐边界框)碰撞检测算法
* @param dino 恐龙对象
* @param obs 障碍物对象
* @return true 如果发生碰撞,false 否则
*/
bool checkCollision(const Dino& dino, const Obstacle& obs) {
return dino.x + DINO_WIDTH > obs.x &&
dino.x < obs.x + OBSTACLE_WIDTH &&
dino.y + DINO_HEIGHT > obs.y;
}
// ============ 游戏循环 ============
/**
* 游戏主循环
* 处理输入、更新状态、绘制画面、检测碰撞
* @param dino 恐龙对象引用
* @param obs 障碍物对象引用
* @param game_state 游戏状态对象引用
*/
void gameLoop(Dino& dino, Obstacle& obs, GameState& game_state) {
while (!game_state.game_over) {
// 1. 处理用户输入(含F1调试开关)
handleInput(dino);
// 2. 更新游戏对象状态
dino.update();
bool needs_reset = obs.update();
// 3. 检查障碍物是否超出屏幕并更新分数
if (needs_reset) {
obs.reset();
game_state.updateScore();
}
// 4. 绘制游戏画面
cleardevice();
drawBackground();
dino.draw();
obs.draw();
drawScore(game_state.score);
drawDebugInfo(dino, obs, game_state); // 绘制调试信息
// 5. 碰撞检测
if (checkCollision(dino, obs)) {
game_state.game_over = true;
}
// 6. 控制帧率
delay_ms(FRAME_DELAY);
}
}
// ============ 初始化与清理 ============
/**
* 初始化游戏
* 创建图形窗口并设置初始状态
*/
void initGame() {
// 设置本地化,支持中文显示
setlocale(LC_ALL, "zh_CN.UTF-8");
// 初始化图形窗口
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
setbkcolor(WHITE);
cleardevice();
srand(time(0)); // 初始化随机数种子
// 设置默认字体
setfont(20, 0, L"微软雅黑");
}
/**
* 清理游戏资源
* 等待用户按键后关闭图形窗口
*/
void cleanupGame() {
outtextxy(10, WINDOW_HEIGHT - 30, L"按任意键退出...");
getch();
closegraph();
}
// ============ 主函数 ============
/**
* 程序入口
* 初始化游戏 -> 运行游戏循环 -> 显示结果 -> 清理资源
*/
int main() {
// 初始化游戏
initGame();
// 创建游戏对象
Dino dino;
Obstacle obs;
GameState game_state;
// 运行游戏循环
gameLoop(dino, obs, game_state);
// 显示游戏结束信息(覆盖调试信息)
cleardevice();
drawBackground();
drawScore(game_state.score);
drawGameOver();
// 清理资源并退出
cleanupGame();
return 0;
}

六、总结
该代码通过模块化设计实现了恐龙跳跃游戏的核心功能,从需求拆解到详细设计,每个功能点都结合了单元测试验证逻辑正确性,最终通过集成测试保证整体流程闭环。编译部署依赖EGE图形库,需注意环境配置,整体实现了"输入-更新-绘制-碰撞-结束"的经典2D游戏循环,满足基础的游戏性和交互性需求。
