Files
flykhan 1be2a5ebe0 docs: 重写 README 详细文档,移除开发人员界面
**README 重写(+231 行)**
  - 参照 UnoGame 风格,包含完整项目说明
  - 功能特性、三平台构建命令(Linux/macOS/Windows)
  - 操作指南表格、玩法说明
  - 完整目录树、核心模块职责详述
  - 场景跳转流程图、计时机制表
  - 版本日志 v16 → v19

**功能移除**
  - 删除 MEMBER 场景(开发人员页面)
  - 移除菜单中"相关人员"按钮,退出按钮上移填补空位
  - 删除 images/开发人员界面.jpg
  - 清理 Game.h / Game.cpp / UI.h 中相关代码
2026-05-03 12:07:02 +08:00

8.0 KiB
Raw Permalink Blame History

纸牌上的坦克大战

基于 C++17 + SDL2 的 2D 坦克对战游戏,支持三种难度、实时战斗与胜负判定。

功能特性

  • 3 档难度:简单(HP 5 / ATK 1)、中等(HP 10 / ATK 2)、困难(HP 15 / ATK 3
  • 实时坦克对战:WASD 移动 + 空格开火,敌人 AI 随机移动与射击
  • 战场交互:撞子弹扣血、踩残骸回血 + 提升攻击力、墙壁阻挡
  • 胜负判定:消灭全部 10 辆敌方坦克获胜,HP 归零失败
  • 背景音乐:循环 BGM 播放(静音自动降级)
  • 跨平台Linux / Windows / macOSCMake 一键构建

开发环境

依赖 版本要求
C++ 17+
CMake 3.10+
SDL2 2.x
SDL2_image 2.x
SDL2_mixer 2.x
SDL2_ttf 2.x
中文字体 wqy-microhei 或 noto-cjk(用于界面文本渲染)

Linux

# Ubuntu / Debian
sudo apt install build-essential cmake \
    libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
    fonts-wqy-microhei

# Fedora
sudo dnf install gcc-c++ cmake \
    SDL2-devel SDL2_image-devel SDL2_mixer-devel SDL2_ttf-devel \
    wqy-microhei-fonts

# 构建
cmake -B build
cmake --build build
./build/tank_battles_on_the_scrap_paper

macOS

brew install cmake sdl2 sdl2_image sdl2_mixer sdl2_ttf
cmake -B build
cmake --build build
./build/tank_battles_on_the_scrap_paper

Windows

# 1. 安装 CMakehttps://cmake.org/download
# 2. 安装 vcpkg 并安装 SDL2 依赖:
vcpkg install sdl2 sdl2-image sdl2-mixer sdl2-ttf

# 构建
cmake -B build -DCMAKE_TOOLCHAIN_FILE=<vcpkg-root>/scripts/buildsystems/vcpkg.cmake
cmake --build build
.\build\Release\tank_battles_on_the_scrap_paper.exe

操作指南

按键 功能
W / ↑ 向上移动
S / ↓ 向下移动
A / ← 向左移动
D / → 向右移动
空格 发射子弹
ESC 返回主菜单

鼠标左键点击按钮进行菜单导航。


玩法说明

  1. 主菜单选择开始游戏(使用当前难度设置)或难度设置
  2. 操控红色坦克在 20×20 地图中移动、射击敌方蓝色坦克
  3. 踩到敌方坦克残骸(is_taken)可 +1 HP(上限 5)并 +1 攻击力
  4. 撞到敌方子弹扣 1 HP
  5. 击败全部 10 辆敌方坦克即胜利;玩家 HP 归零则失败
  6. 子弹射到墙壁消失;敌方子弹射到玩家扣 HP 并消失

项目结构

tank_battles_on_the_scrap_paper/
├── CMakeLists.txt                  # CMake 构建配置
├── README.md
├── tank_battles_on_the_scrap_paper/
│   ├── main.cpp                    # 入口:创建 Game 实例,调用 run()
│   ├── data_config.h               # 全局常量(网格大小、数量上限、枚举)
│   ├── images/                     # 58 张 PNG/JPG 素材
│   ├── music/                      # 背景音乐 bgm.wav
│   └── src/
│       ├── Game.h / Game.cpp       # 状态机主循环 + 游戏逻辑
│       ├── AssetManager.h / .cpp   # 纹理 / 字体 / BGM 加载与缓存
│       ├── Renderer.h / .cpp       # SDL 渲染封装
│       ├── Map.h / .cpp            # 地图数据 + 碰撞检测
│       ├── Tank.h / .cpp           # 坦克实体
│       ├── Bullet.h / .cpp         # 子弹实体
│       └── UI.h                    # 界面按钮区域常量
└── .gitignore

images/music/ 在 CMake 构建时自动拷贝到 build/ 目录,游戏通过 SDL_GetBasePath() 自动定位可执行文件所在目录加载资源。


核心模块

Game — 状态机主循环

职责 说明
场景管理 enum class Scene { MENU, LEVEL, GAME, WIN, LOSE, QUIT } 驱动主循环,彻底消除旧版递归函数跳转导致的栈溢出隐患
事件分发 handleEvents() 统一处理 SDL 事件(退出 / ESC / 鼠标点击),根据当前场景路由到不同按钮命中检测
更新循环 updateGame() 每帧执行:玩家输入 → 玩家移动 / 射击 → 敌人 AI 移动 / 射击 → 子弹移动与碰撞 → 地图同步
渲染调度 renderScene() 根据场景调用对应渲染函数

场景跳转流程

MENU ──开始游戏──→ GAME ──胜利──→ WIN ──返回──→ MENU
  │                  │                    │
  ├──难度设置──→ LEVEL │                    │
  │    │               └──失败──→ LOSE ──返回──┘
  │    └──返回──→ MENU
  └──退出游戏──→ 退出

Map — 地图与碰撞

方法 职责
reset() 初始化 20×20 网格:四周为 WALL,内部为 BLANK
get(x, y) 返回指定坐标的 CELL_Type 枚举值
set(x, y, type) 写入单元,每帧同步坦克位置
peek(x, y, dir) 查看某方向前方一格的内容,供坦克和子弹碰撞检测

Tank — 坦克实体

属性 说明
x, y 网格坐标
hp 生命值(玩家默认 5,敌人由难度决定)
attack 攻击力(决定子弹伤害)
dir 方向(UP/DOWN/LEFT/RIGHT
isTaken 仅敌方:残骸是否已被玩家拾取

方向向量 dx(dir) / dy(dir) 统一处理四方向位移,消除旧版中 4 份复制粘贴的 switch 逻辑。

Bullet — 子弹实体

方法 职责
fire(x, y, dir, dmg) 初始化子弹(位置、方向、伤害值)
peekAt(map) 检测前方一格的地图单元类型
step() 向前移动一格
isLive 是否存活(碰墙/命中后置 false

玩家子弹命中 BLUE_TANK 时遍历敌人数组扣血;敌方子弹命中 RED_TANK 时直接扣玩家 HP。子弹碰 WALL 立即消失。

AssetManager — 资源管理

  • 纹理缓存std::unordered_map<string, SDL_Texture*> 按路径缓存,避免重复加载
  • 字体加载TTF_OpenFont() 带多路径回退(wqy-microhei → wqy-zenhei → noto-cjk
  • BGM 管理Mix_LoadMUS() + Mix_PlayMusic(-1) 循环播放,失败静默降级
  • 生命周期:析构时自动释放全部纹理、字体、音频资源

Renderer — 渲染封装

方法 职责
clear() 填充黑色背景
present() 提交帧缓冲区到屏幕
drawTexture() 渲染纹理到指定矩形区域
drawImage() 加载图片并渲染(通过 AssetManager 缓存)
drawText() TTF 文字渲染(UTF-8 编码,SDL_RenderCopy 绘制)
drawInt() 整数值渲染(std::to_string 后调用 drawText

UI — 界面常量

常量 用途
BTN_START 开始游戏按钮区域 {50, 250, 150, 50}
BTN_LEVEL 难度设置按钮区域 {50, 330, 150, 50}
BTN_QUIT 退出游戏按钮区域 {50, 410, 150, 50}
BTN_EASY/MEDIUM/HARD 难度选择按钮区域
BTN_BACK 返回按钮区域 {30, 550, 150, 50}
hit(rect, mx, my) 鼠标命中检测辅助函数

计时机制

游戏使用 SDL_GetTicks() 替代跨平台不一致的 clock(),各逻辑模块以毫秒为间隔独立刷新:

模块 间隔 说明
玩家移动 100 ms 按住方向键连续移动
玩家射击 500 ms 按住空格连续发射
敌人移动 1000 ms 随机方向移动
敌人射击 3000 ms 每个存活敌人发射一发
玩家子弹 100 ms 逐格前进
敌方子弹 500 ms 逐格前进

版本日志

版本 变更
v19 全面重构:1468 行单文件拆分为 8 个模块,状态机替代递归,全局变量清零,方向向量消除 4x 复制粘贴,修复 EnemiesMove 循环变量遮蔽 bug
v18 从 Windows/EasyX 迁移到 SDL2,支持跨平台;修复启动黑屏(SDL_GetBasePath 自动定位资源目录)
v17 修复返回菜单后 InitMap() 未调用导致地图脏数据
v16 添加全局 BGM 背景音乐循环播放