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