将 10_Manual_ProgressionSystem.md 中不存在的 SkillTreeSO/技能点/技能树解锁 流程,改写为真实实现:技能(FormSkillSO)随形态由 FormController 注入 SkillManager, 施放消耗魂力/灵力;能力通过 AbilityType 位掩码解锁(PlayerStats/AbilityFlags)。 同步更正 MT-PROG-06 的 HasAbility/存档字段引用,并统一 05/07/11 文档措辞为 '形态技能一览(FormSkillPanel)',明确本项目无技能树。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
15 KiB
手动测试 10 · 进程与养成系统
测试类型:Unity Editor 手动测试(Play Mode)
覆盖模块:BaseGames.Skills、BaseGames.Equipment、BaseGames.Quest、BaseGames.Progression、BaseGames.World.Shop
依赖组件:SkillManager、EquipmentManager、QuestManager、AchievementManager、ShopKeeper
场景要求:含存档点、商店 NPC、任务触发器的完整测试场景
快速工具
| 工具 | 用途 | 菜单路径 |
|---|---|---|
| Validate All ScriptableObjects | 遍历所有实现 IValidatable 的 SO,输出验证结果(含 FormSkillSO、QuestDatabaseSO 等) |
BaseGames → Tools → Validate All ScriptableObjects |
注意:PlayModeDebugOverlay 已移除。Geo 注入、能力解锁测试等请通过 Inspector 直接修改对应 SO/Manager 字段,或临时编写 Editor 脚本触发。
Tab 10 的场景摆放工具(Add Quest Trigger、Add Shop NPC)已不再提供;请参照下方各节手动步骤手工创建对应对象。重要:本项目没有"技能点 + 技能树解锁"系统。 技能(
FormSkillSO)随形态绑定,由FormController在切换形态时注入SkillManager,无需花费技能点解锁;SkillTreePanel仅为只读的形态技能一览面板。能力(二段跳/冲刺/形态等)通过AbilityType位掩码解锁(见 MT-PROG-06),不涉及任何技能树。
典型工作流:
- 测试前:
BaseGames → Tools → Validate All ScriptableObjects一键确认 SO 存在;若有缺失,Console 给出路径提示。 MT-PROG-01技能:进入 Play Mode,通过FormController切换形态,确认SkillManager注入对应形态的三个技能,施放消耗魂力/灵力并进入冷却。MT-PROG-03任务:手动放置QuestGiverNPC(见下方步骤),将QuestSO拖入 Inspector,Play Mode 中交互验证。MT-PROG-05商店:手动放置ShopNPC(见下方步骤),通过 Inspector 修改_geo字段给玩家加钱,交互购买。
目录
- 前置数据检查
- MT-PROG-01:形态技能与施放
- MT-PROG-02:装备系统(护符/武器)
- MT-PROG-03:任务系统(QuestManager)
- MT-PROG-04:成就系统(AchievementManager)
- MT-PROG-05:商店系统(Shop)
- MT-PROG-06:能力解锁(AbilityUnlock)
1. 前置数据检查
| 资产 | 路径(示例) | 必要性 | ✓ |
|---|---|---|---|
| FormSkillSO ×N | Assets/_Game/Data/Skills/SKL_*.asset |
技能测试必须(每形态 3 个:魂技+灵技1+灵技2) | ☐ |
| EquipmentSlotConfigSO | Assets/Data/Equipment/SlotConfig.asset |
装备测试必须 | ☐ |
| QuestDatabaseSO | Assets/Data/Quests/ |
任务测试必须 | ☐ |
| AchievementDatabaseSO | Assets/Data/Achievements/ |
成就测试必须 | ☐ |
| ShopInventorySO | Assets/Data/Shop/ |
商店测试必须 | ☐ |
🔧 一键检查 + 资产创建
步骤 1 — 验证资产存在性: 菜单
BaseGames → Tools → Validate All ScriptableObjects
- Console 输出每项 ✅(通过)或 ❌(失败/未找到)
步骤 2 — 创建所有占位 SO(若有缺失): 按照下方步骤 3 手动通过 Project 右键菜单创建对应资产。
步骤 3 — 创建尚未覆盖的数据资产(手动):
资产 创建方法 FormSkillSOProject 右键 → Create → BaseGames → Skills → Form Skill,保存到 Assets/_Game/Data/Skills/,命名SKL_{skillId};如有近战/爆炸判定再配套SKL_{skillId}_HitBox预制体EquipmentSlotConfigSOProject 右键 → Create → BaseGames → Equipment → Slot Config,保存到 Assets/_Game/Data/Equipment/QuestDatabaseSOProject 右键 → Create → BaseGames → Quest → Quest Database,保存到 Assets/_Game/Data/Quests/AchievementDatabaseSOProject 右键 → Create → BaseGames → Progression → Achievement Database,保存到 Assets/_Game/Data/Achievements/ShopInventorySO已由 Create Test Assets 创建为 ShopInventory_Test.asset;点 Inspector 的+按钮添加ShopItem条目步骤 4 — 绑定 Manager 字段(Play Mode 前):
- 找到 Player 上的
SkillManager组件 → Inspector →_formSkillSets数组,按形态(天魂/地魂/命魂)逐项填入soulSkill+spiritSkill1+spiritSkill2(对应FormSkillSO资产)- 找到
ShopNPCGameObject(Tab 10 → 添加商店 NPC)→_inventory字段拖入ShopInventory_Test.asset
MT-PROG-01:形态技能与施放
目的:验证 SkillManager 的形态技能注入、技能施放、冷却管理、魂力/灵力消耗,以及 FormSkillPanel(只读技能一览)的展示。
架构说明(务必先读)
- 技能 =
FormSkillSO资产,不通过技能点解锁。每个形态绑定 3 个技能槽:魂技(soulSkill)+ 灵技1(spiritSkill1)+ 灵技2(spiritSkill2)。FormController在切换形态时回调SkillManager.UpdateSkillSet(...),把当前形态的三个技能注入到输入槽。玩家无须"学习"技能,切到该形态即可用。- 施放消耗的是魂力(SoulPower)或灵力(SpiritPower)(由
FormSkillSO.resourceType决定),不是 MP。FormSkillPanel(形态技能一览面板)只是只读一览:翻页查看各形态的技能图标/名称/描述/消耗/冷却,没有解锁交互、没有技能点、没有节点。- 技能数值可被护符改写,见 MT-PROG-02 与
SkillModifierRegistry。
🔧 资源准备
- 确认 Player 上
SkillManager._formSkillSets已为待测形态填好三个FormSkillSO。- 若待测形态尚未解锁(地魂/命魂),先解锁其形态能力:编辑
PlayerConfigSO.InitialAbilities勾选FormDiHun/FormMingHun,或临时写 Editor 脚本调用PlayerStats.UnlockAbility(AbilityType.FormDiHun)。天魂(FormTianHun)默认初始解锁。- 确认输入动作已绑定:魂技 =
SoulSkillEvent,灵技1 =SpiritSkill1StartedEvent,灵技2 =SpiritSkill2StartedEvent(见InputReaderSO)。
步骤
步骤 A:形态切换注入技能
- 进入 Play Mode,通过
FormController切换到目标形态(切换输入键,或 Inspector 下调用FormController.SwitchForm(FormType.XXX))。 - 观察
SkillManager的当前技能(SoulSkill/Spirit1/Spirit2属性)。
预期:
- 切换形态后,
SkillManager的三个技能引用更新为该形态_formSkillSets中配置的技能。 - 冷却字典
_cooldowns重建并清零(切换后立即可施放)。 - 若切换的是未解锁形态,
FormController.SwitchForm被HasAbility(FormXXX)拦截,技能集不变。
步骤 B:技能施放
- 进入战斗场景,按魂技/灵技1/灵技2 输入键。
预期:
- 技能动画播放(
AnimancerComponent.Play(castAnimation))。 - 资源扣减:
SoulPower/SpiritPower -= effectiveCost(PlayerStats.ConsumeSoulPower/ConsumeSpiritPower)。 - 命中判定生成(若技能配了
SkillHitBoxPrefab,从对象池取实例并Activate)。 - 进入冷却(
effectiveCooldown秒内再次按键无效)。 - 触发
skill_cast反馈预设。
步骤 C:资源不足时
- 使当前魂力/灵力 <
effectiveCost(可在 Inspector 消耗资源或调小上限)。 - 按技能键。
预期:ConsumeXxxPower 返回 false,技能不释放(无动画、无判定、不进冷却)。
步骤 D:技能冷却
- 施放技能后立即再次按同一技能键。
预期:冷却期内 _cooldowns[skill] > 0,不触发;HUD 冷却指示(SoulCooldownRatio)显示剩余比例。
步骤 E:FormSkillPanel 只读一览
- 打开技能一览面板(
FormSkillPanel)。 - 左右翻页浏览各形态,确认当前实际形态被高亮(
_activeFormIndicator)。
预期:面板展示各形态三技能的图标/名称/描述/消耗/冷却;无任何"解锁/锁定"状态或点击解锁交互(纯查看)。
| 检查点 | 期望 | ✓ |
|---|---|---|
| 形态注入 | 切换形态后 SkillManager 三技能引用随之更新 | ☐ |
| 未解锁形态拦截 | 切到未解锁形态被 HasAbility 拦截 | ☐ |
| 技能施放 | 动画播放,判定生成,魂力/灵力扣减 | ☐ |
| 资源不足 | 无法释放,不进冷却 | ☐ |
| 冷却 | 冷却期无法再次使用,比例显示 | ☐ |
| 一览面板 | 只读展示各形态技能,无解锁交互 | ☐ |
MT-PROG-02:装备系统(护符/武器)
目的:验证 EquipmentManager 护符槽管理、装备属性叠加、超出槽数无法装备。
步骤
步骤 A:装备护符
- 打开装备 UI(默认 Tab 键或 E 键进入背包)
- 拖拽/确认护符到空槽位
预期:
- 护符装备成功,护符图标显示在槽位
- 护符效果立即生效(如 HP+20,查看
PlayerStats.MaxHP) EquipmentManager.IsEquipped(amuletId) == true
步骤 B:槽位已满
- 将所有护符槽填满(
maxAmuletSlots个护符) - 尝试装备第
maxAmuletSlots + 1个护符
预期:系统提示"护符栏已满",无法装备(不会覆盖现有护符)。
步骤 C:卸下护符
- 选中已装备的护符,点击"卸下"
预期:
- 护符移回背包
- 护符提供的属性加成撤销(HP 恢复原值)
步骤 D:武器切换(FormController 联动)
- 切换形态(Sky/Earth/Death)
预期:装备的武器 SO 根据形态切换,攻击力/攻击动画随形态变化。
| 检查点 | 期望 | ✓ |
|---|---|---|
| 装备护符 | 效果立即生效,图标显示 | ☐ |
| 槽位限制 | 超出槽数无法装备 | ☐ |
| 卸下护符 | 属性加成撤销 | ☐ |
| 形态武器 | 不同形态武器属性不同 | ☐ |
MT-PROG-03:任务系统(QuestManager)
目的:验证 QuestManager 的任务激活→进度追踪→完成→奖励全流程。
🔧 资源准备
- 在 Tab 10 →
添加任务触发器(QuestTrigger)一键放置QuestTriggerGameObject(含 CapsuleCollider2D)- 在 Inspector 中将
QuestTriggerSO(手动创建:Project 右键 → Create → BaseGames → Quest → QuestSO)拖入QuestTrigger._questToStart- 确认
QuestDatabaseSO中已注册该 QuestSO 条目
步骤
步骤 A:任务激活
- 找到场景中的任务触发器(如 NPC 对话后触发任务)或手动调用
QuestManager.StartQuest(questId) - 打开任务日志 UI
预期:任务出现在"进行中"列表,任务目标文本正确显示。
步骤 B:进度追踪
- 完成部分任务目标(如击杀 X 敌人/收集 X 物品)
- 查看任务日志
预期:任务进度更新(如 "击杀 2/5 只敌人"),EVT_QuestProgressUpdated 事件触发。
步骤 C:任务完成
- 完成所有任务目标
预期:
EVT_QuestCompleted事件触发- 任务移入"已完成"列表
- 奖励自动发放(Geo/能力解锁/道具)
- 存档文件中
SaveData.Quests[questId].IsCompleted == true
| 检查点 | 期望 | ✓ |
|---|---|---|
| 任务激活 | 任务出现在进行中列表 | ☐ |
| 进度追踪 | 完成目标后进度数字更新 | ☐ |
| 完成奖励 | 奖励正确发放 | ☐ |
| 持久化 | 存档中 IsCompleted == true | ☐ |
MT-PROG-04:成就系统(AchievementManager)
目的:验证 AchievementManager 触发条件监听、达成弹窗、持久化。
步骤
- 触发某个成就的条件(如"首次击杀 Boss"、"连续弹反 5 次"等)
预期:
- 屏幕右上角弹出成就解锁通知(
AchievementPopup) - 通知显示成就名称和图标
EVT_AchievementUnlocked触发
- 打开成就列表 UI
预期:该成就显示为已解锁状态(金色)。
- 退出并重新进入 Play Mode
预期:成就状态仍为已解锁(SaveData.Achievements 持久化)。
| 检查点 | 期望 | ✓ |
|---|---|---|
| 触发弹窗 | 条件达成后弹出通知 | ☐ |
| 列表状态 | 成就列表中显示已解锁 | ☐ |
| 持久化 | 重进后仍为已解锁 | ☐ |
MT-PROG-05:商店系统(Shop)
目的:验证 ShopKeeper/ShopInventory 的购买/售出流程、Geo 扣减、背包更新。
🔧 资源准备
- 手动在场景中创建
ShopNPCGameObject,添加CapsuleCollider2D,并挂载ShopNPC组件- 在 Inspector 中将
ShopInventory_Test.asset拖入ShopNPC._inventory- 打开
ShopInventory_Test.asset,在 Inspector 展开_items数组,添加几个ShopItem(配置 itemId、price、count)- Play Mode 中在 Inspector 直接将
PlayerController/GeoManager._geoCount设为 500 快速获取购物用 Geo
步骤
步骤 A:打开商店
- 走到商店 NPC,按交互键
预期:商店 UI 打开,显示 ShopInventorySO 中的物品列表(价格、图标、名称)。
步骤 B:购买物品
- 选择一个 Geo 充足的物品,确认购买
预期:
CurrentGeo -= item.price- 物品出现在背包
- HUD Geo 数量更新
步骤 C:Geo 不足
- 选择价格超过当前 Geo 的物品
预期:购买失败,提示"Geo 不足",Geo 不变。
步骤 D:售出物品
- 在商店卖出背包中的物品
预期:
CurrentGeo += item.sellPrice- 物品从背包移除
- HUD Geo 数量更新
| 检查点 | 期望 | ✓ |
|---|---|---|
| 商店 UI 打开 | 物品列表正确显示 | ☐ |
| 购买成功 | Geo 减少,物品进背包 | ☐ |
| Geo 不足 | 购买失败,Geo 不变 | ☐ |
| 售出 | Geo 增加,物品移除 | ☐ |
MT-PROG-06:能力解锁(AbilityUnlock)
目的:验证特殊能力(DoubleJump、WallCling、Dash 等 AbilityType 位)的解锁与 AbilityGate 联动。
步骤
- 确认某能力(如 DoubleJump)当前未解锁
- 找到对应的能力解锁点(Boss 击败后掉落,或特定区域触发)
- 触发解锁
预期:
EVT_AbilityUnlocked(AbilityType)触发(AbilityTypeEventChannelSO,payload 为解锁的能力位)PlayerStats.HasAbility(ability) == true(位掩码_unlockedAbilities |= ability)- 存档中
SaveData.Player.AbilityFlags含该能力位((uint)_unlockedAbilities) - 对应
AbilityGate自动开启(若当前场景有联动门)
- 测试新解锁的能力(如 DoubleJump:跳跃后再次跳跃)
预期:能力生效(二段跳可用)。
| 检查点 | 期望 | ✓ |
|---|---|---|
| 解锁事件 | EVT_AbilityUnlocked 触发 | ☐ |
| HasAbility == true | PlayerStats.HasAbility 返回 true | ☐ |
| AbilityGate 开启 | 对应能力门自动开启 | ☐ |
| 能力可用 | 新能力实际可使用 | ☐ |
| 持久化 | 存档 AbilityFlags 含该能力位 | ☐ |