基于EGE19.01完成恐龙跳跃游戏-V02-面向对象或结构体的版本

恐龙跳跃游戏(main-副本.cpp)实现全过程

参考

VSCode配置c/c++环境-01-DevC++配置MinGW-w64 8.1.0 编译器

VSCode配置c/c++环境-02-VSCode配置MinGW-w64 8.1.0 编译器

DEV-C++和VsCode配合ege19.01项目

基础环境

开发工具为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递增至地面。
  • 测试步骤:
    1. 初始状态,恐龙Y=300(DINO_GROUND_Y),is_jump=false;
    2. 按下空格键,检查is_jump是否变为true,jump_h=100;
    3. 执行update(),观察Y是否每次减5,直到jump_h=0;
    4. 上升结束后,观察Y是否逐步加5,回到300,is_jump=false;
功能点2:绘制逻辑
  • 设计:使用EGE的bar函数绘制矩形(x,y,x+宽,y+高)表示恐龙。
  • 测试步骤:
    1. 初始化后,恐龙是否在X=50、Y=300位置绘制50×50矩形;
    2. 跳跃过程中,矩形位置是否随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。
  • 测试步骤:
    1. 初始状态,障碍物X=800,速度默认8;
    2. 执行update(),观察X是否每次减速度值;
    3. 当X=-31时,验证update()返回true;
    4. 调用reset(),检查X是否回到800,速度是否在6-9之间(多次调用验证随机性)。
功能点2:绘制逻辑
  • 设计:使用bar函数绘制30×30矩形,位置随X/Y变化。
  • 测试步骤:
    1. 初始状态,障碍物是否在X=800、Y=320位置绘制矩形;
    2. 移动过程中,矩形位置是否随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。
  • 测试步骤:
    1. 初始分数为0,游戏结束状态为false;
    2. 障碍物第一次重置后,检查分数是否变为1;
    3. 多次重置后,分数是否逐次递增;
    4. 调用reset(),验证分数回到0,game_over=false。
功能点2:游戏结束判定
  • 设计:碰撞检测触发时,game_over设为true,退出游戏循环。
  • 测试步骤:
    1. 强制让恐龙与障碍物坐标重叠(如修改恐龙X=800);
    2. 执行碰撞检测后,验证game_over=true;
    3. 游戏循环退出,显示"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格式化字符串,在右上角显示分数。
  • 测试步骤:
    1. 运行程序,验证窗口底部有一条地面线;
    2. 分数为0时,右上角显示"Score: 0",分数增加后文字同步更新。
功能点2:游戏结束文字绘制
  • 设计:drawGameOver()在窗口中间显示"Game Over!"。
  • 测试步骤:
    1. 游戏结束后,验证文字在(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)触发恐龙跳跃。
  • 测试步骤:
    1. 未按键时,恐龙不跳跃;
    2. 按下空格键,触发跳跃;
    3. 按上方向键,触发跳跃;
    4. 按其他键(如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
  • 测试步骤:
    1. 恐龙X=50、Y=300,障碍物X=60、Y=320 → 满足条件,返回true(碰撞);
    2. 恐龙X=50、Y=200(空中),障碍物X=60、Y=320 → dino.y+50=250 < 320,返回false(不碰撞);
    3. 恐龙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,依次执行输入→更新→重置检测→绘制→碰撞检测→帧率控制。
  • 测试步骤:
    1. 运行程序,验证循环持续执行,帧率稳定(约60FPS);
    2. 障碍物重置时,分数同步增加;
    3. 碰撞发生后,循环退出,显示Game Over;
    4. 帧延迟16ms,验证游戏无过快/过慢运行。

9. 初始化与清理模块

功能点1:窗口初始化
  • 设计:initGame()调用initgraph创建窗口,设置白色背景,初始化随机数种子(srand(time(0)))。
  • 测试步骤:
    1. 运行程序,窗口正常弹出,背景为白色;
    2. 多次运行,障碍物速度随机(6-9),验证随机数种子生效。
功能点2:资源清理
  • 设计:cleanupGame()等待按键后关闭窗口(closegraph())。
  • 测试步骤:
    1. 游戏结束后,按下任意键,窗口关闭;
    2. 无内存泄漏(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. 集成测试(模块联动)

  • 流程测试:启动游戏→按空格跳跃→躲避障碍物→分数增加→碰撞后游戏结束→按键退出,全流程无异常;
  • 边界测试:
    1. 恐龙跳跃到最高处时躲避障碍物,验证不碰撞;
    2. 障碍物速度为9(最快)时,恐龙仍可通过跳跃躲避;
    3. 分数累计到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游戏循环,满足基础的游戏性和交互性需求。

相关推荐
星空露珠2 小时前
又双叒叕统计被炸死的lua脚本
开发语言·数据结构·算法·游戏·lua
林鸿群2 小时前
大联盟游戏列表为空解决
游戏
风酥糖4 小时前
Godot游戏练习01-第9节-游戏轮次
游戏·godot
风酥糖4 小时前
Godot游戏练习01-第13节-粒子系统,武器攻击特效
游戏·游戏引擎·godot
dd_669965 小时前
游戏上市公司合同系统实施案例(三):从需求分层到全生命周期管理
游戏·信息可视化·数据可视化
张老师带你学7 小时前
unity船资源,快艇,帆船,游轮
科技·游戏·unity·游戏引擎·模型
云边散步7 小时前
godot2D游戏教程系列二(17)
笔记·学习·游戏
大囚长8 小时前
游戏主机神经纹理压缩与AI重建技术的综合应用方案分析
人工智能·游戏
前端不太难10 小时前
开源社区如何复活一款经典游戏
游戏·开源·状态模式