Files
zeling_v2/Docs/Verification/06_Manual_PlayerFSM_Movement.md

425 lines
14 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.
# 手动测试 06 · 玩家 FSM 与移动系统
> **测试类型**Unity Editor 手动测试Play Mode
> **覆盖模块**`BaseGames.Player`、`BaseGames.Player.States`
> **依赖组件**`PlayerController`、`PlayerMovement`、`AnimancerComponent`、`PlayerStats`
> **场景要求**:包含玩家 Prefab 的测试场景TestRoom.unity地面 Layer = `Ground`
---
## 快速工具
| 工具 | 用途 | 菜单路径 |
|------|------|----------|
| **Place Player** | 在场景中放置带完整组件的玩家 GameObjectPlayerController、PlayerStats、HurtBox 等) | `BaseGames → Scene → Place → Player` |
| **Place Ground Platform** | 放置地面平台BoxCollider2DLayer=Ground | `BaseGames → Scene → Place → Ground Platform` |
| **Place Obstacle (Static)** | 放置静止障碍物(用于蹬墙跳测试墙),手动调整尺寸和位置 | `BaseGames → Scene → Place → Obstacle (Static)` |
| **Place Room Camera** | 放置带 Cinemachine + RoomCamera + CinemachineConfiner2D 的房间相机 | `BaseGames → Scene → Place → Room Camera` |
> **注意**PlayModeDebugOverlay 已移除。Play Mode 运行时调试请使用 `Window → Analysis → EventBus Monitor`(若已集成)或 Console 过滤器查看状态机日志。
**典型工作流**
1. `BaseGames → Scene → Place → Player` 放置玩家;`Place → Ground Platform` 放置地面;`Place → Room Camera` 放置相机。
2. `MT-PLAYER-04` 蹬墙跳测试:`Place → Obstacle (Static)` 在玩家右侧放置垂直墙体,调整位置和尺寸。
3. Play Mode 运行时通过 Inspector 直接修改 HP / Soul 字段,或者利用 Console + EventBus Monitor 观察状态机转换。
---
## 目录
1. [场景搭建要求](#1-场景搭建要求)
2. [MT-PLAYER-01基础移动状态转换](#mt-player-01基础移动状态转换)
3. [MT-PLAYER-02跳跃与下落](#mt-player-02跳跃与下落)
4. [MT-PLAYER-03冲刺地面/空中)](#mt-player-03冲刺地面空中)
5. [MT-PLAYER-04蹬墙滑行与蹬墙跳](#mt-player-04蹬墙滑行与蹬墙跳)
6. [MT-PLAYER-05受击与死亡状态](#mt-player-05受击与死亡状态)
7. [MT-PLAYER-06灵泉治疗](#mt-player-06灵泉治疗)
8. [MT-PLAYER-07形态切换](#mt-player-07形态切换)
9. [MT-PLAYER-08连击链完整性](#mt-player-08连击链完整性)
10. [MT-PLAYER-09存档点复活流程](#mt-player-09存档点复活流程)
---
## 1. 场景搭建要求
在开始任何玩家测试前,确认测试场景包含以下元素:
| 元素 | 说明 | ✓ |
|------|------|---|
| 玩家 Prefab | 挂有 `PlayerController``PlayerMovement``AnimancerComponent``PlayerStats``HurtBox` | ☐ |
| 地面平台 | Layer = `Ground`,带 `Collider2D` | ☐ |
| 墙壁 | 至少一面垂直墙壁Layer = `Ground`,用于蹬墙测试 | ☐ |
| InputReader SO | `PlayerController``_inputReader` 字段已绑定 `InputReaderSO` 资产 | ☐ |
| PlayerStatsSO | `PlayerController``_statsConfig` 字段已绑定 | ☐ |
| PlayerMovementConfigSO | `PlayerMovement``_config` 字段已绑定 | ☐ |
| AnimationConfigSO | `PlayerController``_animConfig` 字段已绑定 | ☐ |
| Physics2D Layer 矩阵 | Player、Ground Layer 碰撞开启 | ☐ |
> **🔧 场景快速搭建**
>
> 1. `BaseGames → Scene → Place → Player` 放置玩家 + `Place → Ground Platform` 生成地面 + `Place → Room Camera` 生成相机
> 2. `MT-PLAYER-04` 蹬墙跳:`Place → Obstacle (Static)` 在玩家右侧放置垂直墙体,调整 Transform 尺寸。
>
> **玩家 Prefab 手动组装(若无预制体)**
>
> 1. 场景 `[Player]` 根节点下创建 `PLY_Player` GameObject
> 2. 添加以下组件:
> - `Rigidbody2D`DynamicFreezeRotationInterpolate
> - `CapsuleCollider2D`尺寸参考美术图层isTrigger=false
> - `AnimancerComponent`(从 Kybernetik.Animancer 包)
> - `PlayerController``BaseGames.Player`
> - `PlayerMovement``BaseGames.Player`
> - `StatusEffectManager``BaseGames.Combat.StatusEffects`
> 3. 创建子 GameObject `HurtBox`isTrigger=trueLayer=PlayerHurtBox并添加 `HurtBox` 组件
> 4. 创建子 GameObject `HitBox_Sword`isTrigger=trueLayer=PlayerHitBox**初始 SetActive(false)**,添加 `HitBox` 组件
> 5. **Inspector 中绑定 SO 字段**
> - `PlayerController._inputReader` → 拖入 `InputReaderSO.asset``Assets/Data/Input/`
> - `PlayerController._statsConfig` → 拖入 `PlayerStats.asset``Assets/Data/Settings/`
> - `PlayerController._animConfig` → 拖入 `AnimationConfig.asset`
> - `PlayerMovement._config` → 拖入 `PlayerMovementConfig.asset`
> 6. 菜单 `BaseGames → Tools → Physics2D Layer Matrix → Check` / **Auto Fix** 确保层碰撞矩阵正确
>
> **一键生成 SO 资产(若尚未创建)**
> 菜单 `BaseGames → Tools → Create Test Assets` 自动生成上述所有 SO 占位资产。
---
## MT-PLAYER-01基础移动状态转换
**目的**:验证 `IdleState → RunState → IdleState` 转换Animancer FSM 动画同步。
### 步骤
1. 进入 Play Mode
2. 玩家站立不动,观察动画
**预期**:播放 `Idle` 动画,无抖动。
3. 按住 **A/D**(或方向键左右)移动
**预期**
- 玩家水平位移,`PlayerMovement.IsGrounded == true`
- 动画切换到 `Run`Animancer 状态可在 Animator 窗口观察)
4. 松开移动键
**预期**
- 玩家减速到静止(`Deceleration` 生效)
- 动画切回 `Idle`
5. 按住移动并朝反方向转向
**预期**
- `SpriteRenderer.flipX` 改变(或 Transform.localScale.x 取反)
- 朝向立即响应,无延迟
| 检查点 | 期望 | ✓ |
|--------|------|---|
| Idle 动画播放 | 无卡帧,无 A-pose | ☐ |
| Run 动画播放 | 移动速度与动画速率匹配 | ☐ |
| 朝向正确 | 面向移动方向 | ☐ |
| Console 无 Error | 0 个红色 Error | ☐ |
---
## MT-PLAYER-02跳跃与下落
**目的**验证跳跃物理弧线、土狼时间Coyote Time、可变跳跃高度提前松键降低高度
### 步骤
**步骤 A普通跳跃**
1. 地面上按 **Space**(跳跃键)
2. 观察轨迹
**预期**:抛物线跳跃弧线,高度约为 `PlayerMovementConfigSO.JumpForce / Gravity`
**步骤 B可变跳跃**
1. 跳跃后立即松开 **Space**
**预期**:玩家跳跃高度降低(`PlayerMovement` 在松键时减少向上速度),动画保持 Jump → Fall 正确过渡。
**步骤 C土狼时间Coyote Time**
1. 让玩家走到平台边缘,**走出平台悬空**
2.`CoyoteTime`(约 0.15s)内按 **Space**
**预期**:跳跃生效(玩家可以从空中起跳),而非立即下落。
**步骤 D下落加速**
1. 跳跃到最高点后松键,等待自然下落
**预期**:下落速度比上升时快(`FallMultiplier` 生效),手感有重量感。
| 检查点 | 期望 | ✓ |
|--------|------|---|
| Jump 动画 | 起跳时播放 Jump 动画 | ☐ |
| Fall 动画 | 下落时切换 Fall 动画 | ☐ |
| 土狼时间有效 | 走出平台后短时内仍可跳跃 | ☐ |
| 落地动画 | 落地瞬间切回 Idle/Run无卡帧 | ☐ |
---
## MT-PLAYER-03冲刺地面/空中)
**目的**:验证 `DashState`(地面)和 `AerialDashState`(空中)的无敌帧、冷却、次数限制。
### 步骤
**步骤 A地面冲刺**
1. 地面上按 **Left Shift**(冲刺键)
**预期**
- 玩家水平快速位移(距离约 `dashDistance`
- 冲刺期间播放 Dash 动画
- 冲刺期间受到敌人攻击**不触发** `HurtState`(无敌帧)
- 冲刺结束后自然过渡 `IdleState``RunState`
**步骤 B空中冲刺**
1. 跳跃后按 **Left Shift**
**预期**
- 空中水平冲刺
- 消耗 `_aerialDashCount`(通常 1 次)
- 落地后次数重置
**步骤 C空中冲刺次数用尽**
1. 空中冲刺 1 次(次数用尽)
2. 再次按冲刺键
**预期**:冲刺不触发(次数为 0 时无反应或有 UI 提示)。
**步骤 D冲刺冷却**
1. 地面冲刺后立即再次按冲刺键
**预期**:冷却期间(`dashCooldown`)无法再次冲刺。
| 检查点 | 期望 | ✓ |
|--------|------|---|
| 地面冲刺位移 | 快速平移 `dashDistance` 距离 | ☐ |
| 无敌帧 | 冲刺期间不受伤 | ☐ |
| 空中冲刺次数 | 次数耗尽后无法再冲 | ☐ |
| 落地重置次数 | 落地后次数恢复 | ☐ |
---
## MT-PLAYER-04蹬墙滑行与蹬墙跳
**目的**:验证 `WallSlideState` 减速下滑、`WallJumpState` 弹离逻辑。
### 步骤
**步骤 A蹬墙滑行**
1. 跳跃接近垂直墙壁,按住朝向墙壁的方向键
2. 玩家贴墙后观察
**预期**
- 下落速度从自由落体减缓到 `wallSlideSpeed`
- 播放 `WallSlide` 动画
**步骤 B蹬墙跳**
1. WallSlide 状态下按 **Space**
**预期**
- 玩家弹离墙壁(方向取反)
- 施加 `wallJumpForce`(包含 X/Y 两个分量)
- 播放 `WallJump` 动画
**步骤 C离墙后方向控制**
1. 蹬墙跳后立即按反向方向键(远离墙壁方向)
**预期**:玩家可控制方向(不被强制锁定朝向太久)。
| 检查点 | 期望 | ✓ |
|--------|------|---|
| WallSlide 减速 | 贴墙后下落速度明显降低 | ☐ |
| WallJump 弹离 | 弹离墙壁,方向翻转 | ☐ |
| 跳跃后可控 | `wallJumpLockTime` 后方向键恢复控制 | ☐ |
---
## MT-PLAYER-05受击与死亡状态
**目的**:验证 `HurtState` 硬直和 `DeadState` 死亡冻结。
### 步骤(需要有能攻击玩家的敌人或调试伤害源)
**步骤 A受击硬直**
1. 让敌人攻击玩家HP 不为 0
2. 观察玩家反应
**预期**
- 受击动画播放
- 硬直期间(`hurtDuration`)玩家无法输入
- 硬直结束后自动恢复 `IdleState`
- HP 减少HUD HP 条更新
**步骤 B死亡**
1. 让玩家 HP 降至 0
**预期**
- 死亡动画播放
- `Rigidbody2D.constraints` 冻结(角色不再受物理影响下移)
- `_onPlayerDied` 事件触发EventBusMonitor 可见)
- 死亡屏幕 UI 出现
| 检查点 | 期望 | ✓ |
|--------|------|---|
| HurtState 动画 | 受击动画播放 `hurtDuration` 秒 | ☐ |
| 硬直期间无法输入 | 按攻击键无反应 | ☐ |
| HP 减少 | HUD HP 条正确减少 | ☐ |
| DeadState 冻结 | 死亡后角色不再移动 | ☐ |
| 死亡 UI | DeathScreen 显示 | ☐ |
---
## MT-PLAYER-06灵泉治疗
**目的**:验证 `SpringState` 治疗动画、灵泉消耗、打断限制。
### 步骤
1. 确保玩家有灵泉(`SpringCount > 0`)且 HP 未满
2.**治疗键**(默认参考 `InputReaderSO` 配置)
**预期**
- 播放治疗动画(`Spring` 动画片段)
- 治疗动画期间不可被打断(敌人攻击触发 `HurtState` 优先级低于 `SpringState` 的硬直保护期)
- 动画结束后 HP 增加(`springHealAmount`
- `SpringCount - 1`HUD 灵泉图标减少
3. 灵泉耗尽后再按治疗键
**预期**:无反应(`SpringCount == 0` 时不进入 `SpringState`)。
| 检查点 | 期望 | ✓ |
|--------|------|---|
| 治疗动画 | Spring 动画播放完整 | ☐ |
| HP 回复 | HP 增加 `springHealAmount` | ☐ |
| 灵泉数量 -1 | HUD 灵泉图标减少 | ☐ |
| 灵泉耗尽 | `SpringCount == 0` 时无法治疗 | ☐ |
---
## MT-PLAYER-07形态切换
**目的**:验证 `FormController` 三形态Sky/Earth/Death切换正确更新外观和事件。
### 步骤
1. 进入 Play Mode
2. 打开 `Window → BaseGames → EventBus Monitor`
3. 按形态切换键(循环切换 Sky → Earth → Death → Sky
**预期(每次切换):**
| 检查点 | 期望 | ✓ |
|--------|------|---|
| 调色板更新 | `SpriteRenderer` 颜色/材质变化Palette Swap | ☐ |
| `EVT_FormChanged` 触发 | EventBusMonitor 显示频道触发 | ☐ |
| `EVT_SkillSetChanged` 触发 | 技能组随形态更新 | ☐ |
| HUD 形态图标 | 形态图标切换到对应形态 | ☐ |
| 武器伤害来源刷新 | 不同形态的武器 SO 绑定正确 | ☐ |
4. 切换到 Earth 形态后存档(与存档点交互)
5. 退出并重新进入 Play Mode加载存档
**预期**:重载后当前形态仍为 Earth`PlayerSaveData.ActiveFormId` 持久化)。
---
## MT-PLAYER-08连击链完整性
**目的**:验证 3 段连击链Attack1 → Attack2 → Attack3、超时重置、空中攻击、下劈/上劈。
### 步骤
**步骤 A3 段地面连击**
1. 地面站立状态,连续快速按 3 次攻击键(间隔在 Combo 窗口内)
**预期**
- 播放 Attack1 → Attack2 → Attack3 三段动画
- 第 3 段结束后返回 Idle不可延续第 4 段
**步骤 B连击超时重置**
1. 按 1 次攻击后等待超时(约 1-2 秒)
2. 再次按攻击键
**预期**:从 Attack1 重新开始Combo 计数重置)。
**步骤 C空中攻击**
1. 跳跃后按攻击键
**预期**:播放 `AirAttack` 动画HitBox 激活(正前方)。
**步骤 D下劈**
1. 跳跃后按 **向下 + 攻击**
**预期**:播放 `DownAttack` 动画;若正下方有敌人,命中后玩家向上反弹(`trampolineForce`)。
**步骤 E上劈**
1. 地面上按 **向上 + 攻击**
**预期**:播放 `UpAttack` 动画HitBox 激活(正上方)。
| 检查点 | 期望 | ✓ |
|--------|------|---|
| 3 段连击 | Attack1→Attack2→Attack3 顺序正确 | ☐ |
| 超时重置 | 超时后从 Attack1 重新开始 | ☐ |
| 空中攻击 | AirAttack 动画,前方 HitBox | ☐ |
| 下劈反弹 | 下方命中后向上弹起 | ☐ |
| 上劈 | UpAttack 动画,上方 HitBox | ☐ |
| 命中灵力增加 | 攻击命中敌人后灵力条增加 | ☐ |
---
## MT-PLAYER-09存档点复活流程
**目的**:端到端验证存档/死亡/复活完整链路。
### 步骤
1. 进入 Play Mode找到场景中的 `SavePoint`(存档点)
2. 走到存档点附近,按交互键(默认 E
3. 确认 Console 出现 `[SaveManager] 存档成功` 日志
4. 让玩家死亡HP 降至 0
5. 在死亡屏幕点击 "重试"(或按确认键)
**预期**
- 玩家复活在存档点位置
- HP 和灵力恢复满值
- 死亡时丢失的 Geo 以 `DeathShade` 形式出现在死亡地点
6. 走到 `DeathShade` 并与其交互
**预期**
- 回收丢失的 Geo
- `DeathShade` 消失
- Geo 数量正确增加
| 检查点 | 期望 | ✓ |
|--------|------|---|
| 存档触发 | SavePoint 交互后存档成功 | ☐ |
| 死亡屏幕 | HP 归零后显示 DeathScreen | ☐ |
| 复活位置 | 复活在存档点,不是死亡地点 | ☐ |
| HP 满值 | 复活后 HP = MaxHP | ☐ |
| DeathShade 出现 | 死亡地点有 DeathShade | ☐ |
| Geo 回收 | 与 DeathShade 交互回收 Geo | ☐ |