# 手动测试 09 · 世界与场景系统 > **测试类型**:Unity Editor 手动测试(Play Mode) > **覆盖模块**:`BaseGames.World`、`BaseGames.World.Map` > **依赖组件**:`RoomController`、`RoomTransition`、`SavePoint`、`DeathShade`、`WorldStateRegistry` > **场景要求**:含多房间连接(至少 2 个 Room Scene),AbilityGate,MovingPlatform,SavePoint --- ## 快速工具 | 工具 | 用途 | 菜单路径 | |------|------|----------| | **Place Save Point** | 放置带 SavePoint 组件和 BoxCollider2D(TriggerZone) 的存档点 | `BaseGames → Scene → Place → Save Point` | | **Place Camera Trigger Zone** | 放置带 CameraTriggerZone 和 BoxCollider2D(TriggerZone) 的摄像机触发区 | `BaseGames → Scene → Place → Camera Trigger Zone` | | **Place Room Camera** | 放置带 Cinemachine + RoomCamera + CinemachineConfiner2D 的房间摄像机 | `BaseGames → Scene → Place → Room Camera` | | **Place Ground Platform** | 放置地面平台(Layer=Ground) | `BaseGames → Scene → Place → Ground Platform` | | **Place Tilemap Ground** | 放置 Grid + Tilemap + CompositeCollider2D(Layer=Ground) | `BaseGames → Scene → Place → Tilemap Ground` | | **Scaffold Room Scene** | 一键生成完整房间场景结构 | `BaseGames → Tools → Scaffold Room Scene` | > **注意**:PlayModeDebugOverlay 已移除。Run Mode 存档调试请直接通过交互键触发存档点,或在 Inspector 中手动调用 `ISaveService.QuickSave()`。 > 房间过渡对象、移动平台、可破坏平台、能力门等复杂对象请参照下方各节的**手动步骤**手工创建。 **典型工作流**: 1. `MT-WORLD-01` 房间过渡:手动创建 `RoomTransition` GameObject,添加 BoxCollider2D Trigger,配置 `_targetSceneAddress`,两端各一个出口(参考下方手动步骤)。 2. `MT-WORLD-02` 存档:`Place → Save Point` 放置存档点,Play Mode 交互键激活;确认文件写入通过文件浏览器查看 `Application.persistentDataPath`。 3. `MT-WORLD-05` 移动平台 / `MT-WORLD-06` 能力门:手动创建对象,参照各节步骤配置组件(无专用菜单命令)。 --- ## 目录 1. [场景结构检查](#1-场景结构检查) 2. [MT-WORLD-01:房间过渡(RoomTransition)](#mt-world-01房间过渡roomtransition) 3. [MT-WORLD-02:存档点交互](#mt-world-02存档点交互) 4. [MT-WORLD-03:死亡阴影(DeathShade)](#mt-world-03死亡阴影deathshade) 5. [MT-WORLD-04:世界状态持久化(WorldStateRegistry)](#mt-world-04世界状态持久化worldstateregistry) 6. [MT-WORLD-05:可破坏平台与移动平台](#mt-world-05可破坏平台与移动平台) 7. [MT-WORLD-06:能力门(AbilityGate)](#mt-world-06能力门abilitygate) 8. [MT-WORLD-07:可收集物(Collectibles)](#mt-world-07可收集物collectibles) 9. [MT-WORLD-08:世界地图显示](#mt-world-08世界地图显示) --- ## 1. 场景结构检查 | 元素 | 说明 | ✓ | |------|------|---| | RoomController | 每个 Room Scene 挂有 `RoomController`,`roomId` 唯一 | ☐ | | RoomTransition | 房间出入口各有 `RoomTransition`,`targetSceneName` 和 `targetTransitionId` 已配置 | ☐ | | SavePoint | 测试场景含至少 1 个 `SavePoint`,挂有 `SavePointSO` 资产 | ☐ | | WorldStateRegistry | 场景挂有 `WorldStateRegistrar` 或 GlobalObject 上有 `WorldStateRegistry` | ☐ | > **🔧 一键搭建世界测试场景** > > | 需求 | 工具 | 操作 | > |------|------|------| > | 基础场景(地面 + 玩家) | `BaseGames → Scene → Place → Player` + `Place → Ground Platform` | 分别放置玩家和地面 | > | 需要存档点(MT-WORLD-02) | `BaseGames → Scene → Place → Save Point` | 放置 SavePoint + 手动绑定事件频道 | > | 需要 2 个 Room 场景(MT-WORLD-01) | 手动创建场景(见下方 MT-WORLD-01 步骤) | 注册到 Build Settings + Addressables | > | 需要死亡阴影(MT-WORLD-03) | 手动创建 `DeathShade` GameObject | 挂 `DeathShade` 组件,设置 `_geoAmount = 50` | > | 需要过渡触发器 | 手动创建 `RoomTransition` GameObject | 挂 BoxCollider2D Trigger + `RoomTransition` 组件 | > > **完整世界测试场景搭建顺序**: > 1. `Place → Player` + `Place → Ground Platform`(地面 + 玩家) > 2. 手动创建 Room_A / Room_B 并注册(仅 MT-WORLD-01 需要) > 3. `Place → Save Point` + 手动创建 DeathShade > 4. 手动创建 RoomTransition 对(若在当前单一场景内测试) > 5. Addressable Batch Tool → 注册 Room_A / Room_B 场景 --- ## MT-WORLD-01:房间过渡(RoomTransition) **目的**:验证 `SceneLoader` + `RoomTransition` 的场景加载/卸载流程(Addressables 异步加载)。 > **🔧 资源准备** > > 1. 手动创建两个测试场景:`File → New Scene → Empty`,保存为 `Assets/_Game/Scenes/Room_A.unity` 和 `Room_B.unity`,添加地面、`RoomController`、出生点、`RoomTransition` 触发器(左右各一),然后 `File → Build Settings → Add Open Scenes` 注册。 > 2. 用 **Addressable Batch Tool** 将 `Room_A.unity` / `Room_B.unity` 注册,Key 建议 = `Room_A` / `Room_B` > 3. 在两个场景中分别打开对应的 `RoomTransition` GameObject,填写 `_targetSceneAddress`: > - `Room_A` 场景的右侧 Transition:`_targetSceneAddress = "Room_B"` > - `Room_B` 场景的左侧 Transition:`_targetSceneAddress = "Room_A"` > 4. 也可在**单一测试场景**中测试(手动创建 RoomTransition 对),但此方案无法测试实际场景卸载/加载 ### 步骤 1. 进入 Play Mode 2. 玩家走入场景边缘 `RoomTransition` 触发区域 **预期(过渡动画开始)**: - `ScreenFader` 黑幕淡入(淡出当前场景) - Console 出现 `[SceneLoader] Loading: RoomB` 类似日志 - `EVT_RoomTransitionStart` 触发(EventBusMonitor) **预期(新场景加载完成)**: - 新场景画面淡入 - 玩家出现在目标 `RoomTransition` 的 `spawnPoint` 位置 - `EVT_RoomTransitionEnd` 触发 3. 立即再次走回上一个 `RoomTransition` **预期**:可无缝往返,无加载错误,HP 和状态保持不变。 | 检查点 | 期望 | ✓ | |--------|------|---| | 黑幕过渡 | 进出场景时有黑幕淡入淡出 | ☐ | | 玩家出现位置 | 出现在目标过渡点的 spawnPoint | ☐ | | 无加载错误 | Console 无 `InvalidKeyException` 或 `NullReferenceException` | ☐ | | 往返正常 | 多次房间切换无内存泄漏或重复 | ☐ | --- ## MT-WORLD-02:存档点交互 **目的**:端到端验证 `SavePoint` 激活→存档→读取全流程。 > **🔧 资源准备(一键)** > > 使用 `BaseGames → Scene → Place → Save Point` 在场景中快速放置存档点,或手动创建(见下方手动步骤)。 > > **手动步骤**: > 1. 创建空 GameObject,挂 `SavePoint`、`CapsuleCollider2D`(isTrigger=true) > 2. 设置 `_savePointId`(唯一字符串,如 `"testroom_SP_01"`) > 3. 将 `EVT_SavePointActivated`(StringEventChannelSO)拖入 `_onSavePointActivated` > > **触发存档**:进入 Play Mode → 走到存档点 → 按 **E 键**(或配置的 Interact 键)激活 > **快速确认**:存档后打开文件浏览器查看 `Application.persistentDataPath` 目录,确认存档文件已生成 ### 步骤 1. 走到 `SavePoint`(场景中有 `SavePoint.cs` 的 GameObject) 2. 按**交互键**(默认 E 或 F) **预期**: - `SavePoint` 播放激活动画/粒子 - `EVT_SavePointActivated` 触发 - `SaveManager.Save()` 被调用 - Console 出现 `[SaveManager] 存档成功` 日志 - 灵泉次数恢复满值(`SpringCount = MaxSpringCount`) 3. 打开存档文件位置验证(`%AppData%\..\LocalLow\[CompanyName]\[AppName]\save.dat`) **预期**:存档文件修改时间与测试时间一致。 4. 退出 Play Mode,再进入 Play Mode(模拟游戏重启) 5. 验证是否从存档点位置开始 **预期**:玩家出现在存档点坐标,HP 保持存档时的值。 | 检查点 | 期望 | ✓ | |--------|------|---| | 激活动画 | SavePoint 激活 VFX/动画播放 | ☐ | | 存档文件更新 | 存档文件时间戳更新 | ☐ | | 灵泉恢复 | SpringCount = MaxSpringCount | ☐ | | 读档位置 | 重启后从存档点出生 | ☐ | --- ## MT-WORLD-03:死亡阴影(DeathShade) **目的**:验证 `DeathShade` 在死亡位置生成、Geo 附着、回收互动。 > **🔧 资源准备** > > **全流程测试(推荐)**: > 1. 确保场景已有存档点(`BaseGames → Scene → Place → Save Point`) > 2. 正常游戏中让玩家死亡 → DeathShade 自动由 `DeathRespawnService` 在死亡位置创建 > > **快速单元测试(无需真实死亡)**: > - 手动创建 `DeathShade` GameObject,挂 `DeathShade` 组件,设置 `_geoAmount = 50`,放置在场景 x=5 位置 > - 进入 Play Mode → 玩家走到 DeathShade 位置 → 观察 Geo 回收交互 > - ⚠ 此为手动占位;完整流程(覆盖测试项 DeathShade 生成位置/第二次死亡覆盖)仍需通过真实死亡触发 ### 步骤 1. 确认玩家携带 **Geo > 0**,存档(与存档点交互) 2. 让玩家**在存档点之外的区域死亡**(HP 归零) 3. 在死亡屏幕选择重试(复活在存档点) **预期(复活后)**: - 玩家出现在存档点 - 死亡位置(Room B 某坐标)出现 `DeathShade` 对象 - `DeathShade` 持有死亡时的 Geo 数量 4. 走到 `DeathShade` 位置,与其交互(进入触发区域) **预期**: - Geo 被回收,玩家 `CurrentGeo` 增加 - `DeathShade` 消失(`Destroy` 或 `SetActive(false)`) 5. 不回收 DeathShade,再次死亡 **预期**:旧 `DeathShade` 被新的取代(只保留最新一次),旧 Geo 丢失。 | 检查点 | 期望 | ✓ | |--------|------|---| | DeathShade 生成位置 | 出现在死亡坐标 | ☐ | | Geo 附着 | 显示正确的 Geo 数量 | ☐ | | 回收 | 交互后 Geo 增加,DeathShade 消失 | ☐ | | 第二次死亡覆盖 | 只保留最新 DeathShade | ☐ | --- ## MT-WORLD-04:世界状态持久化(WorldStateRegistry) **目的**:验证 `WorldStateRegistry` 中场景状态(门开关、敌人死亡、机关激活)在房间切换后正确持久化。 ### 步骤 1. 找到场景中一个可**一次性触发**的机关(如需要击打的开关、某只 One-Shot 敌人) 2. 激活该机关(或击杀该敌人) 3. 记录其 `stateId`(Inspector 可查) 4. 离开本房间,进入另一个房间,再返回本房间 **预期**: - 机关仍处于已激活状态(门仍开启) - One-Shot 敌人不重新生成 - `WorldStateRegistry.GetState(stateId)` 返回 `true` 5. 退出 Play Mode,再进入 Play Mode(读取存档) **预期**:状态仍持久化(`SaveData.WorldStates` 中有对应 `stateId`)。 | 检查点 | 期望 | ✓ | |--------|------|---| | 房间返回后状态 | 机关/敌人状态不重置 | ☐ | | 读档后状态 | 重新进入 Play Mode 后状态仍持久 | ☐ | --- ## MT-WORLD-05:可破坏平台与移动平台 **目的**:验证 `CrumblePlatform`(踩后掉落)和 `MovingPlatform`(循环移动)物理行为。 ### CrumblePlatform(碎裂平台) 1. 站在 `CrumblePlatform` 上 **预期**: - 约 `crumbleDelay` 秒后平台开始抖动 - 抖动后平台消失(Deactivate/Destroy) - 玩家正常下落 2. 等待 `resetTime` 秒 **预期**:平台重新出现(`SetActive(true)` 或重置到初始位置)。 ### MovingPlatform(移动平台) 1. 站在 `MovingPlatform` 上 **预期**: - 平台在两个 waypoint 之间来回循环移动 - 玩家随平台移动(玩家相对平台位置不变,通过 `transform.SetParent` 或速度叠加实现) 2. 从移动平台跳跃 **预期**:跳跃方向和高度正确(平台速度叠加到跳跃速度)。 | 检查点 | 期望 | ✓ | |--------|------|---| | CrumblePlatform 掉落 | 站上后 crumbleDelay 后消失 | ☐ | | CrumblePlatform 重置 | resetTime 后平台重新出现 | ☐ | | MovingPlatform 携带玩家 | 站上平台随之移动 | ☐ | | 平台跳跃 | 速度叠加正确 | ☐ | --- ## MT-WORLD-06:能力门(AbilityGate) **目的**:验证 `AbilityGate` 根据玩家已解锁能力决定是否通行。 ### 步骤 **步骤 A:无对应能力时** 1. 确保玩家**未解锁** `AbilityGate` 要求的能力(如 DoubleJump) 2. 走到 `AbilityGate` **预期**:门保持关闭(碰撞体阻挡玩家),显示所需能力提示。 **步骤 B:解锁能力后** 1. 通过调试工具或正常流程解锁所需能力 2. 再次走到 `AbilityGate` **预期**:门开启(碰撞体禁用或动画播放),玩家可通过。 | 检查点 | 期望 | ✓ | |--------|------|---| | 无能力 | 门阻挡通行 | ☐ | | 有能力 | 门开启可通行 | ☐ | --- ## MT-WORLD-07:可收集物(Collectibles) **目的**:验证地图中固定位置收集物(Geo 堆、道具)的拾取与持久化。 ### 步骤 1. 找到场景中一个 `Collectible` 物品(如固定 Geo 堆) 2. 玩家走过拾取 **预期**: - 拾取动画/音效播放 - 玩家 CurrentGeo 增加 - `Collectible.stateId` 写入 `WorldStateRegistry` 3. 离开房间再返回 **预期**:该收集物不再出现(One-Shot 语义)。 4. 读取存档重进 **预期**:该收集物仍不出现(状态持久化)。 | 检查点 | 期望 | ✓ | |--------|------|---| | 拾取效果 | 动画/音效 + Geo 增加 | ☐ | | 房间返回 | 不再出现 | ☐ | | 读档后 | 仍不出现(持久化) | ☐ | --- ## MT-WORLD-08:世界地图显示 **目的**:验证 `WorldMap` UI 正确显示已探索房间和玩家当前位置。 ### 步骤 1. 在多个房间之间来回切换(探索新房间) 2. 打开世界地图 UI(默认 Tab 键) **预期**: - 已探索的房间在地图上显示(灰色/彩色) - 未探索的房间不显示(或显示为迷雾) - 玩家当前位置有图标标识 3. 找到并激活 `MapPin`(地图标记物,如 Boss 房间标记) **预期**:地图上对应位置出现 `MapPin` 图标(存档后重进仍存在)。 | 检查点 | 期望 | ✓ | |--------|------|---| | 已探索房间显示 | 地图正确显示探索过的房间 | ☐ | | 未探索房间 | 未探索区域显示迷雾或不显示 | ☐ | | 玩家位置标记 | 玩家图标在正确房间 | ☐ | | MapPin 持久化 | 存档后 MapPin 仍存在 | ☐ |