# 手动测试 13 · 玩家能力与技能系统 > **测试类型**:Unity Editor 手动测试(Play Mode) > **覆盖模块**:`BaseGames.Player`、`BaseGames.Player.States`、`BaseGames.Skills` > **依赖组件**:`PlayerController`、`PlayerMovement`、`PlayerStats`、`PlayerCombat`、`FormController`、`SkillManager`、`SpringSystem`、`ParrySystem` > **场景要求**:包含玩家 Prefab 的测试场景,至少一块地面平台、一面垂直墙、若干测试用敌人 --- ## 快速工具 | 工具 | 用途 | 菜单路径 | |------|------|----------| | **Place Player** | 放置完整玩家 GameObject | `BaseGames → Scene → Place → Player` | | **Place Ground Platform** | 放置地面平台(Layer=Ground) | `BaseGames → Scene → Place → Ground Platform` | | **Place Obstacle (Static)** | 放置垂直墙壁(用于抓墙测试) | `BaseGames → Scene → Place → Obstacle (Static)` | | **Place Test Enemy** | 放置静止测试用敌人(带 HurtBox) | `BaseGames → Scene → Place → Test Enemy` | | **Place Room Camera** | 放置 Cinemachine 相机 | `BaseGames → Scene → Place → Room Camera` | --- ## 场景搭建要求 在运行所有测试前,请确认以下清单: | 检查项 | 说明 | ✓ | |--------|------|---| | 玩家 Prefab 已放置 | 带 `PlayerController`、`PlayerMovement`、`PlayerStats`、`PlayerCombat`、`FormController`、`SkillManager`、`SpringSystem`、`ParrySystem`、`HurtBox`、`AnimancerComponent`、`WeaponManager` | ☐ | | 地面平台 | Layer = `Ground`,至少一块宽平台 | ☐ | | 垂直墙壁 | Layer = `Ground`,高度 ≥ 4 格,用于抓墙/蹬墙跳测试 | ☐ | | 测试用敌人 | 带 `HurtBox`、`EnemyBase`,初始静止,用于攻击/Pogo/灵泉充能测试 | ☐ | | InputReaderSO | 已绑定 `_inputReader` 字段 | ☐ | | PlayerMovementConfigSO | 已绑定,含跳跃/冲刺/墙壁相关参数 | ☐ | | PlayerAnimationConfigSO | 已绑定,含所有动画 Clip 资产 | ☐ | | FormConfigSO | 已绑定到 `FormController._config`,三形态 SO 已配置 | ☐ | | SkillManager._formSkillSets | Inspector 中已配置三个 `FormSkillSet`,每项指定对应形态的三个技能 SO | ☐ | | SkillManager._formController | Inspector 中已绑定 `FormController` 引用 | ☐ | | Physics2D Layer 矩阵 | PlayerHitBox ↔ EnemyHurtBox、Player ↔ Ground 碰撞均已开启 | ☐ | | Console 无红色 Error | 进入 Play Mode 前 Error = 0 | ☐ | --- ## 目录 1. [MT-ABILITY-01:地面三连击与攻击缓冲](#mt-ability-01地面三连击与攻击缓冲) 2. [MT-ABILITY-02:上劈(地面 / 空中)](#mt-ability-02上劈地面--空中) 3. [MT-ABILITY-03:下劈与 Pogo 弹起](#mt-ability-03下劈与-pogo-弹起) 4. [MT-ABILITY-04:空中水平攻击](#mt-ability-04空中水平攻击) 5. [MT-ABILITY-05:攻击取消窗口](#mt-ability-05攻击取消窗口) 6. [MT-ABILITY-06:抓墙与高度记忆机制](#mt-ability-06抓墙与高度记忆机制) 7. [MT-ABILITY-07:蹬墙跳(背墙跳 / 对墙跳)](#mt-ability-07蹬墙跳背墙跳--对墙跳) 8. [MT-ABILITY-08:二段跳](#mt-ability-08二段跳) 9. [MT-ABILITY-09:无敌冲刺](#mt-ability-09无敌冲刺) 10. [MT-ABILITY-10:三形态切换](#mt-ability-10三形态切换) 11. [MT-ABILITY-11:三套资源系统](#mt-ability-11三套资源系统) 12. [MT-ABILITY-12:灵泉使用](#mt-ability-12灵泉使用) 13. [MT-ABILITY-13:灵泉充能(击杀积累)](#mt-ability-13灵泉充能击杀积累) 14. [MT-ABILITY-14:魂技能施放](#mt-ability-14魂技能施放) 15. [MT-ABILITY-15:魄技能施放(技能 1 / 2)](#mt-ability-15魄技能施放技能-1--2) 16. [MT-ABILITY-16:弹反系统](#mt-ability-16弹反系统) --- ## MT-ABILITY-01:地面三连击与攻击缓冲 **目的**:验证地面三段连击的前摇/有效帧/后摇三阶段、HitBox 激活时序、攻击缓冲机制。 ### 前置条件 - 玩家站于地面 - 在玩家攻击范围内放置测试用敌人 ### 步骤 A:三段连击完整触发 1. 进入 Play Mode 2. 玩家站立地面,按 **Attack 键**(默认 `J`) **预期**: - 播放第 1 段攻击动画(`AnimationConfigSO.GroundAttacks[0]`) - 动画前摇结束时 HitBox 激活(可在 Scene 视图 Gizmos 中观察 HitBox 矩形出现) - 命中敌人后敌人 HP 减少,玩家方向上有微小横向反嵈位移 3. 动画进入后摇阶段后(有效帧结束),再次按 **Attack 键** **预期**: - 无缝衔接第 2 段攻击动画(`GroundAttacks[1]`) - 第 2 段 HitBox 重新激活 4. 同样方式触发第 3 段 **预期**: - 播放 `GroundAttacks[2]`,第三段攻击完成后动画自然结束 - 玩家返回 `IdleState` 或 `RunState` ### 步骤 B:攻击缓冲(后摇内预输入) 1. 按下 **Attack 键** 触发第 1 段攻击 2. **在后摇期间**(有效帧结束前后)提前再次按 **Attack 键** **预期**: - 第 1 段后摇结束后**自动**衔接第 2 段,无需再次按键 - 缓冲窗口内的输入被 `InputBuffer` 记录并消耗 ### 步骤 C:攻击中断复位 1. 第 1 段攻击完成后等待后摇完全结束(约 1.5 秒不操作) 2. 再次按 **Attack 键** **预期**: - 从第 1 段重新开始,而非继续第 2 段(连击计数已重置) | 检查点 | 期望 | ✓ | |--------|------|---| | 三段动画各不相同 | 三段 Clip 依次切换 | ☐ | | HitBox 仅在有效帧激活 | 前摇期间无碰撞,Gizmos 不显示 HitBox | ☐ | | 命中反嵈 | 命中敌人时玩家微小位移(打击感) | ☐ | | 攻击缓冲生效 | 后摇内按键可自动续接下一段 | ☐ | | 连击计数超时重置 | 久未续接后从第 1 段重新开始 | ☐ | | Console 无 Error | 0 个红色 Error | ☐ | --- ## MT-ABILITY-02:上劈(地面 / 空中) **目的**:验证上劈的组合键判定(Move Y 轴正向 + Attack)、向上 HitBox 激活、空中向下反嵈。 ### 前置条件 - 在玩家正上方位置放置测试用敌人(调高 Y 坐标) ### 步骤 A:地面上劈 1. 玩家站立地面 2. 按住 **Move 上方向键**(`W` 或上方向键,Y 轴正向),同时按 **Attack 键** **预期**: - 触发上劈动画(`AnimationConfigSO.UpAttack` 对应 Clip) - HitBox 在角色**正上方**激活(Scene 视图 Gizmos 可见) - 命中正上方敌人,敌人 HP 减少 - 玩家仍停留地面,无明显上移(地面上劈无空中反嵈) ### 步骤 B:空中上劈与向下反嵈 1. 跳跃至空中 2. 在空中按住 **Move 上方向键** + **Attack 键** **预期**: - 触发上劈动画 - **玩家 Y 轴速度减少约 3(向下反嵈)**,可在 Inspector → `Rigidbody2D.velocity.y` 观察到短暂下移 - 上劈动画结束后进入 `FallState` - 命中敌人后敌人 HP 减少 ### 步骤 C:Move Y 阈值边界 1. 空中仅按轻推上方向(Y 轴输入 < 0.5),同时按 **Attack 键** **预期**: - 触发普通空中水平攻击,而非上劈(Y 阈值未达到 0.5 不判定为上劈) | 检查点 | 期望 | ✓ | |--------|------|---| | 地面上劈 HitBox 在上方 | Scene Gizmos 可见上方碰撞盒 | ☐ | | 空中上劈向下反嵈 | velocity.y 降低约 3,玩家微向下位移 | ☐ | | Y 轴阈值正确 | 轻推不触发上劈 | ☐ | | 动画正确 | 播放上劈专属 Clip | ☐ | --- ## MT-ABILITY-03:下劈与 Pogo 弹起 **目的**:验证下劈仅限空中触发、向下速度施加、命中 Pogo 弹起机制(重置空中能力)、未命中继续下落。 ### 前置条件 - 玩家位于测试敌人正上方(高度 ≥ 3 格) - 测试敌人带有 `HurtBox` 组件 ### 步骤 A:空中下劈命中 Pogo 1. 跳跃至测试敌人正上方 2. 按住 **Move 下方向键**(`S` 或下方向键,Y 轴负向),同时按 **Attack 键** **预期**: - 触发下劈动画(`AnimationConfigSO.DownAttack` 对应 Clip) - 玩家 Y 轴速度被设为约 `-18`,快速向下冲击 - 下方 HitBox 激活 - **命中敌人后**:玩家立即向上弹起(`Pogo Jump`,高度固定),与普通跳跃力度相似 - Pogo 触发后空中冲刺次数重置(`_airDashUsed = false`) - Pogo 触发后空中跳跃次数重置(`AirJumpsLeft` 恢复为最大值) ### 步骤 B:空中下劈未命中 1. 在空旷区域(无敌人/无特殊物体)跳跃 2. 空中使用下劈 **预期**: - 下劈动画和向下速度正常 - **未命中任何目标**:玩家**不弹起**,继续受重力下落至地面 - 落地后进入 `IdleState` ### 步骤 C:地面下劈无效 1. 玩家站立地面 2. 按住 **Move 下方向键** + **Attack 键** **预期**: - **触发普通地面水平攻击**(或无响应),不触发下劈 - 下劈判定限制在空中状态 `!IsGrounded` ### 步骤 D:Pogo 后续接二段跳 1. 解锁二段跳(`PlayerStats.UnlockAbility(AbilityType.DoubleJump)`,可在 Inspector 勾选) 2. 空中下劈命中 Pogo 弹起 3. 弹起途中按 **Jump 键** **预期**: - Pogo 重置空中跳跃次数,可再次使用二段跳 | 检查点 | 期望 | ✓ | |--------|------|---| | 仅限空中触发 | 地面不触发下劈 | ☐ | | 向下冲击速度 | velocity.y ≈ -18 | ☐ | | 命中 Pogo 弹起 | 玩家向上弹起,高度固定 | ☐ | | Pogo 重置冲刺次数 | Pogo 后可再次空中冲刺 | ☐ | | Pogo 重置跳跃次数 | Pogo 后可再次二段跳(已解锁时) | ☐ | | 未命中不弹起 | 继续下落至地面 | ☐ | --- ## MT-ABILITY-04:空中水平攻击 **目的**:验证空中水平攻击(`AirAttackState`)的触发条件、单次攻击、动画、结束返回 FallState。 ### 步骤 A:空中普通攻击 1. 跳跃至空中 2. 按 **Attack 键**(不按方向,或左右方向,Y 轴绝对值 < 0.5) **预期**: - 触发空中水平攻击动画(`AnimationConfigSO.AirAttack`) - HitBox 在角色侧面激活(`AttackDirection.Air`) - 动画完成后进入 `FallState`,继续下落 - 命中敌人 HP 减少 ### 步骤 B:空中攻击不循环 1. 空中连续多次按 **Attack 键** **预期**: - 空中攻击为单次,不进行连段 - 每次须等动画结束后重新输入才能再次攻击 | 检查点 | 期望 | ✓ | |--------|------|---| | 触发空中攻击动画 | 播放 AirAttack Clip | ☐ | | HitBox 侧面激活 | Scene Gizmos 可见侧面碰撞盒 | ☐ | | 结束后 FallState | 攻击完成后继续下落,不卡住 | ☐ | | 单次不循环 | 无法连续多段空中攻击 | ☐ | --- ## MT-ABILITY-05:攻击取消窗口 **目的**:验证攻击后摇期间的取消窗口允许通过跳跃/冲刺打断攻击动作。 ### 步骤 A:攻击后跳跃取消 1. 地面攻击触发第 1 段 2. 在**有效帧结束后、后摇结束前**(取消窗口期间)按 **Jump 键** **预期**: - 立即从 `AttackState` 转入 `JumpState` - 不必等待后摇完全结束即可起跳 - 取消窗口关闭后(后摇末尾)按 Jump 则无法打断 ### 步骤 B:攻击后冲刺取消 1. 地面攻击触发第 1 段 2. 在取消窗口期间按 **Dash 键** **预期**: - 立即进入 `DashState`,打断后摇 ### 步骤 C:取消窗口外不可取消 1. 地面攻击触发第 1 段 2. 在**前摇期间**(有效帧之前)按 **Jump 键** **预期**: - 跳跃**无效**,必须等待取消窗口开放后才可取消 | 检查点 | 期望 | ✓ | |--------|------|---| | 取消窗口内可跳跃 | Jump 立即打断后摇 | ☐ | | 取消窗口内可冲刺 | Dash 立即打断后摇 | ☐ | | 前摇内无法取消 | Jump/Dash 在前摇期间无响应 | ☐ | --- ## MT-ABILITY-06:抓墙与高度记忆机制 **目的**:验证触发条件、无需持续按键维持、高度记忆防无限攀爬逻辑(正常模式静止 / 受限模式下滑)。 ### 前置条件 - 在玩家右侧放置高度 ≥ 6 格的垂直墙壁(Layer=Ground) ### 步骤 A:基本抓墙触发 1. 向右跳跃,在空中接触右侧墙壁 2. 按住 **Move 右方向键**(朝向墙壁方向,X 轴正向) **预期**: - 进入 `WallSlideState` - 播放抓墙动画 - **松开方向键**后角色仍保持抓墙状态(无需持续按键) ### 步骤 B:主动松墙 1. 抓墙状态下按下 **Move 左方向键**(反方向键) **预期**: - 立即解除抓墙,进入 `FallState` ### 步骤 C:正常模式(抓墙高度 ≤ wallGrabY)—— 静止悬挂 1. 跳跃至墙壁中部,触发抓墙(记录 `wallGrabY`) 2. 松开方向键,观察角色是否移动 **预期**: - 角色**静止悬挂**,Y 坐标不变 - 可以在此状态触发蹬墙跳 ### 步骤 D:受限模式(抓墙高度 > wallGrabY)—— 持续下滑且无法蹬墙跳 1. 在 C 的基础上,直接跳跃后**更高处再次贴墙**(同一面墙,但 Y 坐标高于 `wallGrabY`) 2. 触发抓墙,松开方向键 **预期**: - 角色**持续向下滑动**(受限模式) - **无法触发蹬墙跳**(按 Jump 键无效) > **💡 操作技巧**:从地面站立后直接跳上更高位置并贴墙,此时 Y > wallGrabY(因为上一次落地已重置),也可以先蹬墙跳到更高处再贴同一面墙来制造 Y > wallGrabY 的情况。 ### 步骤 E:落地重置 wallGrabY 1. 完成 C 的抓墙后蹬墙跳离墙,落地 2. 再次跳跃贴同一面墙(高度与 C 中相同) **预期**: - 落地后 `wallGrabY` 已重置,该高度重新进入**正常模式**(静止悬挂,可蹬墙跳) ### 步骤 F:贴另一面墙重置 wallGrabY 1. 在步骤 C 中抓右侧墙壁后,蹬墙跳至左侧墙壁 2. 在左侧墙壁比之前更高处抓墙 **预期**: - 切换到另一面墙壁时 `wallGrabY` 重置为新值 - 该高度为正常模式(可静止,可蹬墙跳) | 检查点 | 期望 | ✓ | |--------|------|---| | 朝墙方向键触发抓墙 | 接触墙 + 朝墙输入 → WallSlideState | ☐ | | 无需持续按键 | 松开方向键后仍保持抓墙 | ☐ | | 反向键松墙 | FallState | ☐ | | 正常模式静止 | Y 坐标不变,不下滑 | ☐ | | 受限模式下滑 | 高于 wallGrabY 时持续下滑 | ☐ | | 受限模式无蹬墙跳 | Jump 键在受限模式无效 | ☐ | | 落地重置 | 落地后 wallGrabY 清零 | ☐ | | 换墙重置 | 抓另一面墙时记录新 wallGrabY | ☐ | --- ## MT-ABILITY-07:蹬墙跳(背墙跳 / 对墙跳) **目的**:验证背墙跳(Away)和对墙跳(Toward)的方向判定与施力、视为第一段跳、可变高度支持。 ### 前置条件 - 玩家处于正常抓墙状态(高度 ≤ wallGrabY,可触发蹬墙跳) ### 步骤 A:背墙跳(Away Jump) 1. 抓右侧墙壁,**不按任何水平方向键**(或按左键,即反方向) 2. 按 **Jump 键** **预期**: - 玩家朝**远离右墙方向**弹出(向左上方),播放 WallJumpAway 动画 - 弹出后有约 0.2-0.3 秒的水平输入锁定(`_inputLockTimer`),锁定结束后可自由控制水平方向 - `AirJumpsLeft` 恢复(视为第一段跳) ### 步骤 B:对墙跳(Toward Jump) 1. 抓右侧墙壁,按住 **Move 右方向键**(朝墙方向) 2. 按 **Jump 键** **预期**: - 玩家朝**朝向右墙方向弹出**(向右上方),施力偏向竖直,水平分量较小 - 播放 WallJumpToward 动画 - 同样视为第一段跳,`AirJumpsLeft` 恢复 ### 步骤 C:蹬墙跳后可接二段跳(已解锁时) 1. 背墙跳后,在空中再次按 **Jump 键** **预期**: - 若已解锁二段跳,此时可触发二段跳(`AirJumpsLeft > 0`) - 若未解锁,按 Jump 无效 ### 步骤 D:蹬墙跳可变高度 1. 背墙跳后立即松开 **Jump 键** **预期**: - 跳跃高度降低(同普通跳跃可变高度,提前松键截断上升速度) ### 步骤 E:受限模式下无蹬墙跳 1. 使玩家抓墙高度 > wallGrabY(受限模式) 2. 按 **Jump 键** **预期**: - 蹬墙跳不触发(按 Jump 在受限模式无反应) | 检查点 | 期望 | ✓ | |--------|------|---| | 背墙跳方向正确 | 远离墙壁斜上方弹出 | ☐ | | 对墙跳方向正确 | 朝墙壁斜上方弹出,更偏垂直 | ☐ | | 输入锁定(背墙跳) | 弹出后短时无法控制水平方向 | ☐ | | 视为第一段跳 | AirJumpsLeft 重置 | ☐ | | 可接二段跳 | 蹬墙跳后可再次按 Jump(已解锁) | ☐ | | 可变高度 | 提前松键降低弹跳高度 | ☐ | | 受限模式无效 | 受限抓墙时 Jump 无响应 | ☐ | --- ## MT-ABILITY-08:二段跳 **目的**:验证二段跳的解锁门控、空中二次起跳、高度低于一段跳、可变高度支持。 ### 前置条件 - 在 `PlayerStats` Inspector 中确认 `AbilityType.DoubleJump` 是否已解锁 - **步骤 A 使用未解锁状态,步骤 B 及以后需先解锁** ### 步骤 A:未解锁时空中 Jump 无效 1. 确认二段跳**未解锁**(`AbilityFlags` 中无 `DoubleJump`) 2. 跳跃至空中 3. 在下落途中按 **Jump 键** **预期**: - Jump 无效,不产生二次跳跃 ### 步骤 B:解锁后空中二段跳 1. 在 Inspector 中勾选 `DoubleJump` 解锁(或调用 `Stats.UnlockAbility(DoubleJump)` 通过 Console) 2. 跳跃至空中,在最高点前或下落时按 **Jump 键** **预期**: - 触发二段跳动画(`DoubleJump` Clip) - 玩家再次向上弹起,高度明显低于一段跳 - `AirJumpsLeft` 由 1 减为 0 3. 二段跳后再次按 **Jump 键** **预期**: - 无响应(`AirJumpsLeft == 0`,无更多空中跳跃机会) ### 步骤 C:二段跳可变高度 1. 二段跳后立即松开 **Jump 键** **预期**: - 跳跃高度降低(提前松键截断上升速度) ### 步骤 D:落地重置二段跳次数 1. 使用二段跳后落地 2. 再次跳跃,在空中按 **Jump 键** **预期**: - `AirJumpsLeft` 已重置(落地时 `ResetAirJumps()` 调用),可再次使用二段跳 | 检查点 | 期望 | ✓ | |--------|------|---| | 未解锁时无效 | 空中 Jump 无响应 | ☐ | | 解锁后可二段跳 | 空中第二次 Jump 触发 | ☐ | | 二段跳高度低于一段 | 弹起高度明显更低 | ☐ | | 二段跳次数限制 | 第三次 Jump 无响应 | ☐ | | 可变高度 | 提前松键降低高度 | ☐ | | 落地后重置 | 落地后可再次二段跳 | ☐ | --- ## MT-ABILITY-09:无敌冲刺 **目的**:验证无敌冲刺的解锁门控、冲刺期间无敌帧(不受伤害)、独立冷却机制。 ### 前置条件 - 在 `PlayerStats` Inspector 中确认 `AbilityType.InvincibleDash` 是否已解锁 - 在测试场景中放置一个会持续攻击的测试敌人(或使用调试工具手动给玩家造成伤害) ### 步骤 A:未解锁时冲刺无无敌帧 1. 确认无敌冲刺**未解锁** 2. 在敌人攻击范围内进行冲刺 **预期**: - 冲刺期间受到伤害,进入 `HurtState`(基础冲刺无无敌帧) ### 步骤 B:解锁后冲刺无敌帧 1. 解锁 `InvincibleDash` 2. 在敌人攻击方向上冲刺穿越 **预期**: - 冲刺期间**不受伤害**(`Stats.IsInvincible == true` 持续 `DashInvincibilityDuration` 约 0.20s) - 无敌期间 Inspector 中 `IsInvincible` 字段为 true - 无敌帧结束后恢复正常受伤判定 ### 步骤 C:无敌冲刺独立冷却 1. 无敌冲刺后立即再次冲刺 **预期**: - 第 2 次冲刺可正常触发(冲刺本身冷却 ≈ 0.4s) - 但第 2 次冲刺**无无敌帧**(无敌帧有独立冷却,需等待更长时间恢复) - Inspector 中 `_invincibilityCooldownTimer > 0` 时不授予无敌 | 检查点 | 期望 | ✓ | |--------|------|---| | 未解锁无无敌 | 冲刺中受伤进入 HurtState | ☐ | | 解锁后有无敌帧 | 冲刺期间 IsInvincible=true,不受伤 | ☐ | | 无敌帧持续时间 | 约 0.20s 后 IsInvincible 变 false | ☐ | | 独立冷却 | 短时间内第 2 次冲刺无无敌帧 | ☐ | --- ## MT-ABILITY-10:三形态切换 **目的**:验证三形态切换的输入响应、武器/动画随形态更新、技能集随形态更新。 ### 前置条件 - `FormConfigSO` 中三个 FormSO(天魂/地魂/命魂)已配置,各有不同武器 Prefab - `SkillManager._formSkillSets` 数组已填写三形态对应的技能 SO - `WeaponManager` 已绑定 `FormController` ### 步骤 A:切换至天魂(SwitchSkyForm) 1. 进入 Play Mode,玩家默认形态 2. 按 **SwitchSkyForm 键**(默认 `1`) **预期**: - `FormController.CurrentForm.formType == TianHun` - 武器切换到天魂武器 Prefab(`WeaponManager.ActiveWeapon` 更新) - `SkillManager` 中 `_soulSkill`、`_spirit1`、`_spirit2` 替换为天魂对应技能 - HUD 形态颜色/图标更新(若已实现 UI 订阅) - Console 无 Error ### 步骤 B:切换至地魂(SwitchEarthForm) 1. 按 **SwitchEarthForm 键**(默认 `2`) **预期**:同 A,武器和技能集更换为地魂版本。 ### 步骤 C:切换至命魂(SwitchDeathForm) 1. 按 **SwitchDeathForm 键**(默认 `3`) **预期**:同 A,武器和技能集更换为命魂版本。 ### 步骤 D:切换后攻击使用新武器 1. 切换形态后立即攻击 **预期**: - HitBox 来自新形态武器实例 - 伤害值使用新形态武器 `WeaponSO.Damage` ### 步骤 E:重复切换同一形态 1. 当前已在天魂,再次按 **SwitchSkyForm 键** **预期**: - 无副作用(不重复切换,也不报错) - `OnFormChanged` 事件视实现可能不触发(取决于 `FormController` 是否有相同形态判断) | 检查点 | 期望 | ✓ | |--------|------|---| | 形态正确更新 | CurrentForm.formType 对应键值 | ☐ | | 武器随形态切换 | WeaponManager.ActiveWeapon 更新 | ☐ | | 技能集随形态切换 | SkillManager _soulSkill/_spirit1/_spirit2 更新 | ☐ | | 攻击使用新武器 | 新武器 HitBox 生效 | ☐ | | Console 无 Error | 0 个红色 Error | ☐ | --- ## MT-ABILITY-11:三套资源系统 **目的**:验证灵力、魄元、灵泉次数三者相互独立、各自消耗/恢复逻辑。 ### 步骤 A:灵力(SoulPower)—— 攻击积累 1. 在 Inspector 中观察 `PlayerStats._currentSoulPower`(初始 = 0) 2. 攻击命中测试敌人多次 **预期**: - 每次命中后 `_currentSoulPower` 增加 - 不影响 `_currentSpiritPower` 和 `_currentSpringCharges` 3. 消耗至 0(触发魂技能,见 MT-ABILITY-14) **预期**: - `_currentSoulPower` 降低 - `_currentSpiritPower` 不变 ### 步骤 B:魄元(SpiritPower)—— 时间恢复 1. 使 `_currentSpiritPower` 低于上限(触发魄技能消耗) 2. 等待约 3 秒 **预期**: - `_currentSpiritPower` 每秒自动增加(`SpiritRegenRate` 配置值) - 不影响 `_currentSoulPower` 和 `_currentSpringCharges` ### 步骤 C:灵泉次数(SpringCharges)—— 三者独立 1. 攻击积累灵力,同时魄元自然恢复,灵泉次数保持不变 **预期**: - 三个字段**完全独立变动**,互无影响 | 检查点 | 期望 | ✓ | |--------|------|---| | 灵力随攻击命中增加 | 每次命中 SoulPower 增加 | ☐ | | 魄元随时间自动恢复 | 未满时 SpiritPower 逐秒增加 | ☐ | | 三者独立 | 任一变化不影响其他两项 | ☐ | --- ## MT-ABILITY-12:灵泉使用 **目的**:验证灵泉使用的前置条件(地面 + 有充能)、HP 恢复、Overlay 动画、充能消耗。 ### 前置条件 - `PlayerStats._currentSpringCharges > 0`(至少 1 次充能) - 玩家 HP 不满(降低 HP 以观察回复效果) ### 步骤 A:地面使用灵泉 1. 玩家站立地面(`IsGrounded == true`) 2. HP 低于上限 3. 按 **UseSpring 键**(默认 `E`) **预期**: - 播放 Overlay 动画(Layer 1 叠加于 Base Layer 之上),不打断移动动画 - `_currentSpringCharges` 减少 1 - `PlayerStats.CurrentHP` 增加(`SpringHealAmount` 配置值) - 动画结束后玩家返回正常状态 ### 步骤 B:空中使用灵泉无效 1. 跳跃至空中(`IsGrounded == false`) 2. 按 **UseSpring 键** **预期**: - **无响应**(`OnUseSpring` 检测到非地面,直接返回) ### 步骤 C:充能为 0 时无效 1. 确认 `_currentSpringCharges == 0` 2. 地面按 **UseSpring 键** **预期**: - **无响应**(充能为 0 时不进入 `SpringState`) ### 步骤 D:Overlay 动画不打断移动 1. 地面移动中按 **UseSpring 键** **预期**: - 使用灵泉 Overlay 动画在 Layer 1 播放 - 玩家可继续移动(Layer 0 移动动画继续) | 检查点 | 期望 | ✓ | |--------|------|---| | 地面使用成功 | HP 增加,ChargesCount 减 1 | ☐ | | 空中无效 | 空中按键无任何响应 | ☐ | | 充能为 0 无效 | 无充能时按键无响应 | ☐ | | Overlay 动画叠加 | Layer 1 动画播放,Layer 0 不中断 | ☐ | --- ## MT-ABILITY-13:灵泉充能(击杀积累) **目的**:验证 `SpringSystem` 通过 EVT_EnemyDied 事件积累点数、达到阈值后自动增加灵泉次数。 ### 前置条件 - 场景内有可被击杀的测试敌人 - `SpringSystem` 组件挂在玩家或管理器 GameObject 上,`_onEnemyDied` 字段已绑定 `EVT_EnemyDied` SO 事件资产 - 在 Inspector 中调低 `PlayerStatsSO.SpringKillThreshold`(如设为 2)以方便观察 ### 步骤 A:击杀积累点数 1. 攻击并击杀第 1 个测试敌人 **预期**: - EVT_EnemyDied 事件触发 - `PlayerStats._springKillPoints` 增加 1(可在 Inspector 观察) 2. 再次击杀一个敌人 **预期**: - `_springKillPoints` 增加至阈值 - 自动调用 `RestoreSpringCharges(1)`,`_currentSpringCharges` 增加 1 - `_springKillPoints` 清零 ### 步骤 B:充能上限限制 1. 将 `_currentSpringCharges` 调至上限(`MaxSpringCharges`) 2. 继续击杀敌人 **预期**: - `_springKillPoints` 继续积累 - 当 `_currentSpringCharges == MaxSpringCharges` 时,`RestoreSpringCharges` 不超过上限 ### 步骤 C:存档点恢复次数至上限 1. 消耗 1 次灵泉 2. 与测试场景中的存档点 GameObject 交互(或调用 `Stats.RestoreOnSave()`) **预期**: - `_currentSpringCharges` 恢复至 `MaxSpringCharges` - `_springKillPoints` 清零(存档点重置积累点) | 检查点 | 期望 | ✓ | |--------|------|---| | EVT_EnemyDied 触发点数积累 | 击杀后 _springKillPoints 增加 | ☐ | | 达阈值自动 +1 次充能 | 积累满后 ChargesCount 增加,点数清零 | ☐ | | 充能不超上限 | MaxSpringCharges 限制 | ☐ | | 存档点恢复至上限 | ChargesCount = MaxSpringCharges | ☐ | --- ## MT-ABILITY-14:魂技能施放 **目的**:验证 `SkillManager` 响应 SoulSkill 输入、消耗灵力、播放技能动画、冷却限制。 ### 前置条件 - 当前形态已配置 `FormSkillSet.soulSkill`(有效的 `FormSkillSO` 资产) - `PlayerStats._currentSoulPower` 达到技能消耗量以上 ### 步骤 A:灵力充足时施放魂技能 1. 攻击敌人积累足够灵力(`_currentSoulPower ≥ soulSkill.Cost`) 2. 按 **SoulSkill 键**(默认 `Q`) **预期**: - 技能动画播放(`FormSkillSO.animationClip`) - `_currentSoulPower` 减少 `soulSkill.Cost` - 技能特效实例化(若 `FormSkillSO` 中配置了 VFX) - 技能进入冷却(`_cooldowns[soulSkill]` 开始计时) ### 步骤 B:冷却中无法再次施放 1. 技能施放后立即再次按 **SoulSkill 键** **预期**: - 无响应(冷却剩余 `> 0` 时拒绝施放) ### 步骤 C:灵力不足时无法施放 1. `_currentSoulPower < soulSkill.Cost`(可通过消耗后立即尝试) 2. 按 **SoulSkill 键** **预期**: - 无响应(灵力不足,`SkillManager` 内部判断拒绝) ### 步骤 D:形态切换后技能变更 1. 切换到另一个形态 2. 按 **SoulSkill 键** **预期**: - 施放的是**新形态**的魂技能(不同动画、不同效果) | 检查点 | 期望 | ✓ | |--------|------|---| | 灵力充足时施放成功 | 动画播放,SoulPower 减少 | ☐ | | 冷却中无法施放 | 再次按键无响应 | ☐ | | 灵力不足无法施放 | 资源不足时无响应 | ☐ | | 形态切换后使用新技能 | 切换形态后施放新形态魂技能 | ☐ | --- ## MT-ABILITY-15:魄技能施放(技能 1 / 2) **目的**:验证 `SpiritSkill1`/`SpiritSkill2` 的消耗魄元、独立冷却、形态切换更新。 ### 前置条件 - 当前形态已配置 `FormSkillSet.spiritSkill1` 和 `spiritSkill2` - `PlayerStats._currentSpiritPower` 充足 ### 步骤 A:施放魄技能 1 1. 按 **SpiritSkill1 键**(默认 `R`,长按触发 Start,松开触发 Cancelled) **预期**: - 技能动画播放 - `_currentSpiritPower` 减少 `spiritSkill1.Cost` - 若技能为蓄力型,按住触发蓄力动画,松开触发释放 ### 步骤 B:施放魄技能 2 1. 按 **SpiritSkill2 键**(默认 `F`) **预期**: - 施放魄技能 2(不同动画/效果) - 消耗独立的魄元量(`spiritSkill2.Cost`) ### 步骤 C:技能 1 和技能 2 冷却独立 1. 依次施放技能 1 和技能 2 **预期**: - 技能 1 冷却中**不影响**技能 2 的施放(`_cooldowns` 字典各自独立计时) ### 步骤 D:魄元自动恢复后可再次施放 1. 魄技能消耗魄元后等待恢复 2. 再次施放 **预期**: - 魄元恢复至足够后可再次施放(`SpiritRegenRate` 自动回复) | 检查点 | 期望 | ✓ | |--------|------|---| | 技能 1 施放成功 | 动画播放,SpiritPower 减少 | ☐ | | 技能 2 施放成功 | 不同动画,SpiritPower 减少 | ☐ | | 冷却独立 | 技能 1 冷却不阻止技能 2 | ☐ | | 形态切换后使用新技能 | 新形态的魄技能 1/2 生效 | ☐ | | 魄元恢复后可再施放 | 等待后 SpiritPower 恢复,可用 | ☐ | --- ## MT-ABILITY-16:弹反系统 **目的**:验证 `ParrySystem`/`ParryState` 的触发窗口、弹反成功判定、灵力奖励、护盾恢复。 ### 前置条件 - 场景中有能发动近战攻击的测试敌人(或使用 Debug 工具手动触发弹反机会) - `ParrySystem` 已绑定 `InputReaderSO` - `ShieldComponent` 已配置(若需验证护盾恢复) ### 步骤 A:弹反触发与成功 1. 敌人发起攻击时,在攻击即将命中前按下**弹反键**(`Parry`,默认 `K`) **预期**: - 进入 `ParryState`,播放弹反动画 - 若在弹反窗口内成功拦截敌人攻击: - 弹反命中判定成功,敌人受到弹反反馈(硬直或被弹飞) - `PlayerStats._currentSoulPower` 增加(`ParryInfo.SoulGained`) - `ShieldComponent.OnParrySuccess()` 调用(护盾恢复) - 玩家**不受任何伤害**(弹反期间有效) ### 步骤 B:弹反窗口外受伤 1. 弹反动画开始后**等待窗口结束**(约 0.2-0.3s 后),此时再受到敌人攻击 **预期**: - 玩家正常受伤,进入 `HurtState`(弹反窗口已关闭) ### 步骤 C:空弹反(无敌人攻击) 1. 地面不受攻击时按弹反键 **预期**: - 播放弹反动画 - 弹反窗口结束后自动返回 `IdleState` - 无报错,无副作用 | 检查点 | 期望 | ✓ | |--------|------|---| | 弹反动画播放 | ParryState 正确进入 | ☐ | | 弹反成功不受伤 | 窗口内拦截,玩家 HP 不减少 | ☐ | | 灵力奖励 | SoulPower 增加 SoulGained 值 | ☐ | | 护盾恢复 | ShieldComponent.OnParrySuccess 调用 | ☐ | | 窗口外受伤 | 窗口关闭后受攻击进入 HurtState | ☐ | | 空弹反无副作用 | 无敌人攻击时弹反后正常返回 Idle | ☐ | --- ## 附录 A:Inspector 调试辅助字段 以下字段在 Play Mode 下可在 Inspector 中实时观察,便于调试: | GameObject | 组件 | 字段 | 含义 | |-----------|------|------|------| | Player | `PlayerController` | `_dbg_CurrentState` | 当前 FSM 状态名 | | Player | `PlayerController` | `_dbg_IsGrounded` | 是否落地 | | Player | `PlayerController` | `_dbg_AirJumpsLeft` | 剩余空中跳跃次数 | | Player | `PlayerController` | `_dbg_CanDash` | 当前是否可冲刺 | | Player | `PlayerController` | `_dbg_IsInvincible` | 当前是否无敌 | | Player | `PlayerStats` | `_currentHP` | 当前 HP | | Player | `PlayerStats` | `_currentSoulPower` | 当前灵力 | | Player | `PlayerStats` | `_currentSpiritPower` | 当前魄元 | | Player | `PlayerStats` | `_currentSpringCharges` | 当前灵泉次数 | | Player | `PlayerStats` | `_springKillPoints` | 当前灵泉积累点 | | Player | `FormController` | `CurrentForm` | 当前形态 SO | | Player | `SkillManager` | `_soulSkill` | 当前魂技能 | | Player | `SkillManager` | `_spirit1` | 当前魄技能 1 | | Player | `SkillManager` | `_spirit2` | 当前魄技能 2 | --- ## 附录 B:常见问题排查 | 问题现象 | 可能原因 | 排查步骤 | |---------|---------|---------| | 攻击无 HitBox 效果 | WeaponManager 武器未实例化 | Inspector 检查 `WeaponManager.ActiveHitBoxInstance` 是否为 null | | 形态切换后技能未更新 | `SkillManager._formController` 未赋值 | 确认 Inspector 中已拖入 `FormController` 引用,并检查 `_formSkillSets` 数组长度 ≥ 3 | | 灵泉使用无响应 | `UseSpringEvent` 未绑定 | 检查 `InputReaderSO` 中 UseSpring Action 名称拼写,确认 PlayerController 已订阅 | | 抓墙后立即滑落 | WallDetector 未检测到墙 | 检查墙壁 Layer 是否为 `Ground`,`PlayerWallDetector` 的 `wallLayer` 掩码包含该 Layer | | 蹬墙跳无效 | 受限模式(高于 wallGrabY) | 在受限模式(下滑状态)时蹬墙跳设计上不可用,属预期行为 | | 魄技能冷却独立验证失败 | 技能 SO 资产共用同一实例 | 确认技能 1、技能 2 使用的是**不同** `FormSkillSO` 资产(不同 `.asset` 文件) | | Pogo 未弹起 | DownAttackState `OnDownHitConfirmed` 未订阅 | 检查 `PlayerCombat._currentHitBoxInstance` 是否正确订阅,Console 查看 `HandleWeaponChanged` 是否调用 | | Console 出现 NullReferenceException | Inspector 中某 SO/组件字段未赋值 | 运行时在 `PlayerController.Awake` 的 `Debug.Assert` 输出中查看具体缺失字段 |