325 lines
12 KiB
Markdown
325 lines
12 KiB
Markdown
# 手动测试 08 · 敌人系统
|
||
|
||
> **测试类型**:Unity Editor 手动测试(Play Mode)
|
||
> **覆盖模块**:`BaseGames.Enemies`、`BaseGames.Enemies.AI`、`BaseGames.Enemies.Navigation`
|
||
> **依赖组件**:`EnemyBase`、`EnemyCombat`、`EnemyMovement`、`BehaviorDesigner`、`PathBerserker2d`
|
||
> **场景要求**:已烘焙 NavSurface,至少包含近战/远程/飞行三种敌人各一只
|
||
|
||
---
|
||
|
||
## 快速工具
|
||
|
||
| 工具 | 用途 | 菜单路径 |
|
||
|------|------|----------|
|
||
| **Place Enemy (Basic)** | 放置带 EnemyBase、EnemyStats、HurtBox、HitBox_Body 的基础敌人;多次调用可摆放多种变体,然后手动调整组件 | `BaseGames → Scene → Place → Enemy (Basic)` |
|
||
| **Place Nav Surface** | 在场景中放置 PathBerserker2d NavSurface 对象 | `BaseGames → Scene → Place → Nav Surface` |
|
||
| **Place Ground Platform** | 放置地面平台(Layer=Ground) | `BaseGames → Scene → Place → Ground Platform` |
|
||
|
||
> **NavSurface 烘焙**:在 Inspector 中找到 `NavSurface` 组件,点击 **Bake** 按钮(无对应菜单命令)。
|
||
> **注意**:PlayModeDebugOverlay 已移除。运行时状态效果测试请通过配置带效果的敌人攻击,或代码调用 `StatusEffectManager.Apply()`。
|
||
|
||
**典型工作流**:
|
||
1. 测试前:`Place → Ground Platform` 生成地面 + `Place → Enemy (Basic)` 放置近战 / 远程 / 飞行三种敌人(多次调用,手动调整组件和配置)。
|
||
2. **Add Enemy Variants**:`Place → Enemy (Basic)` 多次,分别调整 `EnemyStats` 和行为树为远程 / 飞行变体。
|
||
3. Inspector NavSurface → **Bake** 烘焙寻路数据(相比手动查找 Inspector 更直接)。
|
||
4. 状态效果测试(`MT-ENEMY-04`):配置带毒/燃烧效果的敌人攻击,或通过代码调用;观察 VFX 变化和 Console 事件。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [NavSurface 烘焙检查](#1-navsurface-烘焙检查)
|
||
2. [MT-ENEMY-01:近战敌人 AI 基础行为](#mt-enemy-01近战敌人-ai-基础行为)
|
||
3. [MT-ENEMY-02:远程敌人(RangedEnemy)](#mt-enemy-02远程敌人rangedenemy)
|
||
4. [MT-ENEMY-03:飞行敌人(FlyingEnemy)](#mt-enemy-03飞行敌人flyingenemy)
|
||
5. [MT-ENEMY-04:敌人霸体与击退](#mt-enemy-04敌人霸体与击退)
|
||
6. [MT-ENEMY-05:敌人死亡与掉落](#mt-enemy-05敌人死亡与掉落)
|
||
7. [MT-ENEMY-06:敌人配额管理](#mt-enemy-06敌人配额管理)
|
||
8. [MT-ENEMY-07:Boss 战切换流程](#mt-enemy-07boss-战切换流程)
|
||
|
||
---
|
||
|
||
## 1. NavSurface 烘焙检查
|
||
|
||
PathBerserker2d 的寻路**完全依赖烘焙数据**,未烘焙时敌人原地站立无响应。
|
||
|
||
> **🔧 敌人场景快速搭建**
|
||
>
|
||
> 1. `BaseGames → Scene → Place → Ground Platform` 生成地面 + `Place → Player` 放置玩家(若尚未搭建)
|
||
> 2. `BaseGames → Scene → Place → Enemy (Basic)` 多次调用,放置近战 / 远程 / 飞行三种敌人:
|
||
> - **MeleeEnemy**:保持默认配置(EnemyBase + EnemyStats + EnemyMovement + HurtBox)
|
||
> - **RangedEnemy**:手动添加 `ShootPoint` 子 GameObject,调整 EnemyStats 为远程变体 SO
|
||
> - **FlyingEnemy**:手动修改 Rigidbody2D → Kinematic,`gravityScale = 0`
|
||
>
|
||
> 三只敌人均需在 Inspector 中绑定 Behavior Designer 行为树资产(`BehaviorTree._externalBehavior` 字段)
|
||
> 3. Inspector NavSurface 组件 → 点击 **Bake** 烘焙近战 + 远程敌人的寻路数据
|
||
>
|
||
> **注意**:`FlyingEnemy` 无需 NavSurface 寻路;`RangedEnemy` 需要 NavSurface 才能进行保距移动。
|
||
|
||
**检查步骤**:
|
||
|
||
1. 在测试场景中选中挂有 `NavSurface` 组件的 GameObject
|
||
2. Inspector 中找到 `NavSurface` 组件,点击 **Bake**
|
||
3. Scene 视图中地面显示**蓝绿色半透明网格** → 烘焙成功
|
||
|
||
**提示**:若 Gizmo 不可见,点击 Scene 视图右上角 `Gizmos` → 确认 PathBerserker2d 相关项已勾选。
|
||
|
||
---
|
||
|
||
## MT-ENEMY-01:近战敌人 AI 基础行为
|
||
|
||
**目的**:验证近战敌人的巡逻 → 追击 → 攻击 → 返回 Behavior Designer 行为树。
|
||
|
||
> **🔧 前置检查**
|
||
> - `Place → Enemy (Basic)` 已放置 `MeleeEnemy` 并挂载所有必要组件
|
||
> - Inspector 中 `BehaviorTree._externalBehavior` 字段已绑定行为树资产(手动拖入)
|
||
> - NavSurface 已烘焙(Inspector → Bake 按钮)
|
||
|
||
### 步骤
|
||
|
||
**步骤 A:巡逻行为**
|
||
|
||
1. 进入 Play Mode
|
||
2. 玩家保持在敌人**视野范围外**(超过 `detectionRange`)
|
||
3. 观察敌人
|
||
|
||
**预期**:
|
||
- 敌人在巡逻点之间来回移动
|
||
- 播放 `Walk`/`Patrol` 动画
|
||
- Console 无 PathBerserker2d 寻路错误
|
||
|
||
**步骤 B:追击行为**
|
||
|
||
1. 玩家进入敌人视野(`detectionRange` 内且无遮挡物)
|
||
2. 观察敌人反应
|
||
|
||
**预期**:
|
||
- 敌人停止巡逻,转向玩家方向
|
||
- 播放 `Run`/`Chase` 动画
|
||
- 以最优路径追击玩家(PathBerserker2d 动态路径)
|
||
|
||
**步骤 C:攻击行为**
|
||
|
||
1. 玩家进入敌人攻击范围(`attackRange` 内)
|
||
2. 观察敌人攻击
|
||
|
||
**预期**:
|
||
- 播放攻击动画(Behavior Designer 行为树触发攻击节点)
|
||
- `EnemyCombat.HitBox` 激活,若玩家在判定范围内触发伤害
|
||
- 攻击后进入冷却(`attackCooldown`)
|
||
|
||
**步骤 D:掉失目标后返回**
|
||
|
||
1. 玩家跑出敌人追击范围(`chaseRange` 外)
|
||
2. 观察敌人
|
||
|
||
**预期**:
|
||
- 敌人停止追击,返回初始位置或巡逻路径
|
||
- 返回后恢复巡逻动画
|
||
|
||
| 检查点 | 期望 | ✓ |
|
||
|--------|------|---|
|
||
| 巡逻动画 | 视野外敌人来回巡逻 | ☐ |
|
||
| 追击切换 | 玩家进入视野后立即追击 | ☐ |
|
||
| 攻击命中 | 攻击范围内玩家 HP 减少 | ☐ |
|
||
| 返回巡逻 | 丢失目标后返回初始位置 | ☐ |
|
||
| 无寻路错误 | Console 无 PathBerserker2d 相关 Error | ☐ |
|
||
|
||
---
|
||
|
||
## MT-ENEMY-02:远程敌人(RangedEnemy)
|
||
|
||
**目的**:验证 `RangedEnemy` 的投射物发射、移动闪避、最优射击位置。
|
||
|
||
> **🔧 前置检查**
|
||
> - `BaseGames → Scene → Place → Enemy (Basic)` 已放置 `RangedEnemy`(调整 EnemyStats 为远程变体,位置 x=8)
|
||
> - Inspector 中为 `RangedEnemy` 配置行为树资产(保距 + LOS 检测 + 发射节点)
|
||
> - `RangedEnemy._shootPoint` 子 Transform 已手动添加
|
||
> - 场景有静止障碍物(`Place → Obstacle (Static)`)以便观察子弹碰撞
|
||
|
||
### 步骤
|
||
|
||
1. 确认测试场景中有 `RangedEnemy` 实例(挂有 `EnemyCombat` 组件)
|
||
2. 进入 Play Mode,玩家接近远程敌人
|
||
|
||
**步骤 A:保持距离**
|
||
|
||
**预期**:远程敌人尝试保持在 `preferredDistance` 附近,玩家靠近时后退。
|
||
|
||
**步骤 B:发射投射物**
|
||
|
||
**预期**:
|
||
- 在 `shootRange` 内发射投射物(`LinearProjectile` 或 `ArcProjectile`)
|
||
- 投射物从 `_shootPoint` Transform 位置发出
|
||
- 命中玩家触发伤害
|
||
|
||
**步骤 C:LOS(视线)检测**
|
||
|
||
1. 让玩家躲在墙壁后面(无视线)
|
||
2. 观察远程敌人是否仍然发射
|
||
|
||
**预期**:无视线时,敌人停止发射(LOS 检测生效)。
|
||
|
||
| 检查点 | 期望 | ✓ |
|
||
|--------|------|---|
|
||
| 保持距离 | 玩家靠近时后退 | ☐ |
|
||
| 发射投射物 | 视线内发射子弹命中玩家 | ☐ |
|
||
| LOS 遮挡 | 墙后无视线时停止发射 | ☐ |
|
||
|
||
---
|
||
|
||
## MT-ENEMY-03:飞行敌人(FlyingEnemy)
|
||
|
||
**目的**:验证 `FlyingEnemy` 的空中移动、俯冲攻击、不受地面 NavSurface 约束。
|
||
|
||
> **🔧 前置检查**
|
||
> - `BaseGames → Scene → Place → Enemy (Basic)` 已放置 `FlyingEnemy`(位置 (3,5,0))
|
||
> - `Rigidbody2D.gravityScale = 0`,`bodyType = Kinematic`(手动在 Inspector 设置)
|
||
> - 配置行为树资产(直线追击 + 俯冲攻击,无需 NavSurface)
|
||
|
||
### 步骤
|
||
|
||
1. 确认场景有 `FlyingEnemy` 实例(Kinematic RB,gravityScale=0)
|
||
2. 进入 Play Mode
|
||
|
||
**步骤 A:空中悬停/巡逻**
|
||
|
||
**预期**:
|
||
- 飞行敌人在空中自由移动,不受地形限制
|
||
- 可以穿越平台上下(不像地面敌人需要寻路绕路)
|
||
|
||
**步骤 B:俯冲攻击**
|
||
|
||
**预期**:
|
||
- 锁定玩家后俯冲攻击
|
||
- 攻击后飞回起始高度,继续攻击循环
|
||
|
||
**步骤 C:不被地面碰撞阻挡**
|
||
|
||
1. 让飞行敌人在追击路径中有地形障碍
|
||
|
||
**预期**:飞行敌人从障碍物上方飞过(不被地面碰撞体阻挡)。
|
||
|
||
| 检查点 | 期望 | ✓ |
|
||
|--------|------|---|
|
||
| 空中自由移动 | 不受地面寻路限制 | ☐ |
|
||
| 俯冲攻击 | 正确识别玩家位置并俯冲 | ☐ |
|
||
| 绕过地形 | 从障碍上方飞过 | ☐ |
|
||
|
||
---
|
||
|
||
## MT-ENEMY-04:敌人霸体与击退
|
||
|
||
**目的**:验证 `EnemyPoiseComponent` 在普通攻击下保持动画,重攻击才打断。
|
||
|
||
### 步骤
|
||
|
||
**步骤 A:普通连击不打断**
|
||
|
||
1. 对有较高霸体的敌人进行连击
|
||
|
||
**预期**:若连击伤害低于霸体 `breakLevel`,敌人动画**不被打断**,继续执行 AI 行为。
|
||
|
||
**步骤 B:重攻击打断**
|
||
|
||
1. 对同一敌人使用 `BreakLevel` 高的攻击(如下劈 `DownAttack` 或特殊技能)
|
||
|
||
**预期**:敌人进入受击硬直,AI 行为树暂停。
|
||
|
||
**步骤 C:击退效果**
|
||
|
||
1. 攻击有击退(`knockbackForce > 0`)的攻击
|
||
|
||
**预期**:敌人被击退一定距离(`knockbackForce` 方向与大小),不会穿墙。
|
||
|
||
| 检查点 | 期望 | ✓ |
|
||
|--------|------|---|
|
||
| 普通攻击不打断 | 霸体保护时 AI 行为继续 | ☐ |
|
||
| 重攻击打断 | BreakLevel > Poise 时触发硬直 | ☐ |
|
||
| 击退物理 | 被击退后不穿越地形 | ☐ |
|
||
|
||
---
|
||
|
||
## MT-ENEMY-05:敌人死亡与掉落
|
||
|
||
**目的**:验证敌人 HP 归零后的死亡流程、Geo 掉落、`LootResolver`。
|
||
|
||
### 步骤
|
||
|
||
1. 将敌人 HP 降至 0
|
||
|
||
**预期**:
|
||
- 死亡动画播放
|
||
- 死亡动画结束后 GameObject 从场景移除(或 `SetActive(false)` 归还对象池)
|
||
- `LootTableSO` 根据权重随机掉落 Geo 或其他物品
|
||
|
||
2. 在掉落的 Geo 上移动玩家
|
||
|
||
**预期**:Geo 被拾取,玩家 `CurrentGeo` 增加,HUD Geo 数量更新。
|
||
|
||
3. 重新进入场景(房间切换后返回)
|
||
|
||
**预期**:根据 `WorldStateRegistry` 配置,死亡敌人是否重生(可配置的一次性/可重生区别)。
|
||
|
||
| 检查点 | 期望 | ✓ |
|
||
|--------|------|---|
|
||
| 死亡动画 | HP 归零后播放死亡动画 | ☐ |
|
||
| 清理 | 动画结束后 GameObject 消失或归池 | ☐ |
|
||
| Geo 掉落 | 掉落 Geo 可拾取,HUD 更新 | ☐ |
|
||
| LootTable | 掉落物符合权重概率 | ☐ |
|
||
|
||
---
|
||
|
||
## MT-ENEMY-06:敌人配额管理
|
||
|
||
**目的**:验证 `EnemyQuotaManager` 限制同屏激活敌人数量,防止性能劣化。
|
||
|
||
### 步骤
|
||
|
||
1. 打开 Inspector,找到场景中的 `EnemyQuotaManager`
|
||
2. 记录当前 `maxActiveEnemies` 配置值(如 8)
|
||
3. 进入 Play Mode,触发大量敌人(通过多个 EnemySpawner)
|
||
|
||
**预期**:
|
||
- 同屏激活的敌人不超过 `maxActiveEnemies`
|
||
- 超出配额的敌人保持待机(Deactivate 或等待)
|
||
- 当已激活敌人死亡后,待机敌人激活补充
|
||
|
||
| 检查点 | 期望 | ✓ |
|
||
|--------|------|---|
|
||
| 不超过配额 | 激活敌人数量 ≤ maxActiveEnemies | ☐ |
|
||
| 死亡后补充 | 敌人死亡后待机敌人激活 | ☐ |
|
||
|
||
---
|
||
|
||
## MT-ENEMY-07:Boss 战切换流程
|
||
|
||
**目的**:验证 Boss 战触发的 GameState 切换(Gameplay → BossFight)、Boss 血条 UI 显示。
|
||
|
||
### 步骤
|
||
|
||
1. 进入有 Boss 触发器的场景或房间
|
||
2. 玩家进入 Boss 房间触发器
|
||
|
||
**预期**:
|
||
- `EVT_BossFightStarted` 事件触发(EventBusMonitor 可见)
|
||
- `GameManager` 状态切换到 `BossFight`
|
||
- Boss HP 血条 UI 出现(大型 HP 条 + Boss 名称文字)
|
||
- 背景音乐切换为 Boss 战曲目
|
||
|
||
3. 将 Boss HP 降至 0
|
||
|
||
**预期**:
|
||
- `EVT_BossFightEnded` 事件触发(`victory = true`)
|
||
- GameManager 切回 `Gameplay` 状态
|
||
- Boss 血条 UI 隐藏
|
||
- 胜利 VFX/音效播放
|
||
|
||
| 检查点 | 期望 | ✓ |
|
||
|--------|------|---|
|
||
| Boss 战触发 | 进入触发器后 GameState = BossFight | ☐ |
|
||
| Boss HP 条 | Boss 血条 UI 正确显示 | ☐ |
|
||
| BGM 切换 | 战斗音乐正确播放 | ☐ |
|
||
| Boss 死亡 | 胜利后状态恢复 Gameplay | ☐ |
|
||
| BossProgressTracker | Console 中 Boss 击败状态记录 | ☐ |
|