# 世界层级划分规范:大地图区域 / 房间 / 摄像机区域 > **适用范围**:`BaseGames.World`、`BaseGames.Camera`、`BaseGames.World.Map` > **关联代码**:`RoomController`、`RoomTransition`、`MapRoomDataSO`、`MapDatabaseSO`、`RoomCamera`、`CameraTriggerZone`、`CameraStateController` --- ## 一、三层结构总览 ``` 大地图区域(Region) ← 纯分类标签,无独立 Scene └── 房间(Room) ← 一个 Addressable Unity 场景,加载/卸载的最小单元 └── 摄像机区域(Camera Zone) ← Room 内部的视野子区域 ``` 这三层粒度**严格单向包含**,不允许跨层引用(如 CameraZone 不应直接引用 Region 数据)。 --- ## 二、大地图区域(Region) ### 定义 Region 在代码中仅体现为 `MapRoomDataSO.RegionId`(`string` 字段),**没有独立的 Scene 或 GameObject**。 它是一个纯粹的分类标签,由各子系统按需读取。 ### 命名约定 ``` Forest、Ruins、Underground、Castle、Abyss ... ``` 使用 PascalCase 英文单词,与 `MapRoomDataSO` 资产文件路径保持一致: ``` Assets/ScriptableObjects/Map/Room_Forest_01.asset → RegionId = "Forest" Assets/ScriptableObjects/Map/Room_Ruins_03.asset → RegionId = "Ruins" ``` ### 各系统对 RegionId 的用途 | 系统 | 行为 | |------|------| | `MapPanel` | 按 `RegionId` 分组格子,可染不同底色 | | `BGMController` | 监听 `EVT_RoomEntered`,比较前后 `RegionId` 决定是否淡入淡出新 BGM | | `DifficultyScalerSO` | 可按 Region 配置不同难度曲线 | ### 跨区域事件(已实现) `MapManager.OnRoomEntered` 每次进房时比较 `RegionId`,区域变化时广播 `_onRegionChanged`(`StringEventChannelSO`,建议资产命名 `EVT_RegionChanged`)。在 Inspector 中将对应的 `StringEventChannelSO` 资产拖入 `_onRegionChanged` 槽位即可启用。 订阅示例: ```csharp // BGMController — 区域切换时淡入新 BGM _onRegionChanged?.Subscribe(regionId => PlayBGMForRegion(regionId)).AddTo(_subs); // MapPanel — 可选:高亮当前区域格子 _onRegionChanged?.Subscribe(_ => RefreshRegionHighlight()).AddTo(_subs); ``` 也可通过接口直接查询当前区域(无需订阅事件): ```csharp var region = ServiceLocator.Get().CurrentRegionId; ``` --- ## 三、房间(Room) ### 定义 Room = **一个 Addressable Unity 场景**,通过 `SceneLoader` 以 Additive 模式加载,是地图加载/卸载的最小单元。 每个 Room 场景必须包含: - `RoomController`(挂在 `[RoomRoot]` 下):持有 `_roomId`、`_spawnPoints[]`、`_roomCamera` - 至少一个 `PlayerSpawnPoint`(`TransitionId` 与上游 `RoomTransition._targetTransitionId` 对应) - 对应的 `MapRoomDataSO` 资产(`RoomId` 与 `_roomId` 一致) ### 划分原则 **应该单独成一个 Room 的情况:** | 情况 | 原因 | |------|------| | 地形拓扑彻底断开(门、竖井、地下入口) | 自然的 Additive 场景边界 | | 有独立存档点(`SavePoint`) | 复活逻辑依赖 `roomId` 定位存档点 | | Boss 竞技场 | `IsBossRoom = true`,`GameManager` 监听 `EVT_BossFightStarted` | | 商店 / 特殊 NPC 房间 | `IsShop = true`,地图图标独立标记 | | 独立 BGM 区域 | `RegionId` 变化或同区域内 BGM 不同 | | 地图格子单元(`GridPosition` 有意义的独立位置) | `MapPanel` 以格为单位渲染 | **不要过度切分的情况:** | 情况 | 建议 | |------|------| | 无地图意义的短走廊 | 合并到相邻功能房间,避免触发无意义的 Additive 加载/卸载 | | 同一视野无特殊逻辑的连续平台 | 合并,减少 `RoomTransition` 触发频率 | ### 命名约定 ``` Room_{RegionId}_{序号两位} 示例:Room_Forest_01、Room_Ruins_03、Room_Boss_Forest_01 ``` Addressable Key 与场景名保持一致,`MapRoomDataSO.RoomId` 与两者均一致。 --- ## 四、摄像机区域(Camera Zone) ### 定义 Camera Zone = Room 内部的**视野子区域**,由以下组件组合定义: | 组件 | 职责 | |------|------| | `RoomCamera` | 包装 `CinemachineCamera`,持有该区域的 `RoomVisibleArea`、偏移、混合配置 | | `RoomVisibleArea` | `PolygonCollider2D`,作为 Cinemachine Confiner2D 的限位边界 | | `CameraTriggerZone` | `BoxCollider2D` 触发器,玩家进入时调用 `ICameraService.SwitchRoom(targetCamera)` | `CameraStateController` 同时只激活一个 `RoomCamera`,切换时旧相机 `Priority = 0`(停用),新相机 `Priority = 15`(激活)。 ### 划分原则 **需要新建 Camera Zone 的情况:** | 情况 | 配置方式 | |------|---------| | 房间内有高低落差或宽窄通道 | 各区域放独立 `CameraTriggerZone` + `RoomCamera`(独立 `RoomVisibleArea`) | | Boss 竞技场入场锁定视野 | 进入 Arena 触发器切换到关闭 Follow 的固定 `RoomCamera` | | 可选路径(上路/下路)视野不同 | 每条路各放 1 个触发器 | **不需要额外 Camera Zone 的情况:** | 情况 | 配置方式 | |------|---------| | 房间完全平坦无特殊视野需求 | 仅 `RoomController._roomCamera` 1 个,无需 `CameraTriggerZone` | ### Room 内 Camera Zone 的标准层级结构 ``` [Room_Forest_01] ├── RoomController (_roomCamera → Cam_Main) ├── Cameras/ │ ├── Cam_Main (RoomCamera + RoomVisibleArea) ← 入口默认视野 │ ├── Cam_Narrow (RoomCamera + RoomVisibleArea) ← 密道区 │ └── Cam_Arena (RoomCamera + RoomVisibleArea) ← Boss 场 ├── Triggers/ │ ├── Trigger_Narrow (CameraTriggerZone → Cam_Narrow) │ └── Trigger_Arena (CameraTriggerZone → Cam_Arena) ├── SpawnPoints/ │ ├── SpawnPoint_Entry (transitionId: "entry_main") │ └── SpawnPoint_Arena (transitionId: "entry_arena") └── [GameplayObjects] ... ``` ### 混合配置复用 不同 Camera Zone 切换时的过渡动画通过 `CameraBlendProfileSO` 配置: ``` Assets/ScriptableObjects/Camera/Blend_Default.asset → EaseInOut 0.5s(大多数房间切换) Assets/ScriptableObjects/Camera/Blend_BossArena.asset → Cut 0s(Boss 入场瞬切) Assets/ScriptableObjects/Camera/Blend_Slow.asset → EaseInOut 1.5s(过场/剧情用) ``` --- ## 五、完整示例 ``` Region: Forest(森林区域) │ ├── Room: Room_Forest_01(入口区) │ RoomCamera: 1个,无子区域 │ RoomTransition → Room_Forest_02(右侧出口) │ RoomTransition → Room_Ruins_01(跨区域出口,需钥匙) │ ├── Room: Room_Forest_02(主干道 + 密道) │ RoomCamera: Cam_Main(平台区宽视野) │ RoomCamera: Cam_Narrow(树干夹缝,窄限位) │ CameraTriggerZone → Cam_Narrow(进入密道触发) │ RoomTransition → Room_Forest_Boss_01 │ └── Room: Room_Forest_Boss_01(Boss 竞技场) RoomCamera: Cam_Entrance(进门走廊) RoomCamera: Cam_Arena(Boss战,固定锁定视野) CameraTriggerZone → Cam_Arena(进入竞技场触发) IsBossRoom = true → GameManager 监听 EVT_BossFightStarted ``` --- ## 六、编辑器工具 - **`BaseGames → Camera → Room Camera Setup`**:扫描当前场景所有 `RoomCamera` / `CameraTriggerZone`,显示绑定状态并提供一键修复。新建 Room 后务必用此工具检查是否存在未绑定的 Camera Zone。 - **`MapDatabaseSO.GetRoom(roomId)`**:已内置懒加载字典索引(O(1) 查找),无需手动维护索引。