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

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

231 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 纸牌上的坦克大战
基于 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
```bash
# 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
```bash
brew install cmake sdl2 sdl2_image sdl2_mixer sdl2_ttf
cmake -B build
cmake --build build
./build/tank_battles_on_the_scrap_paper
```
### Windows
```powershell
# 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 背景音乐循环播放 |