摄像机区域的架构改动

This commit is contained in:
2026-05-15 14:47:24 +08:00
parent 1b37297585
commit f264329751
3591 changed files with 1687228 additions and 446503 deletions

View File

@@ -0,0 +1,196 @@
# 世界层级划分规范:大地图区域 / 房间 / 摄像机区域
> **适用范围**`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<IMapService>().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 0sBoss 入场瞬切)
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_01Boss 竞技场)
RoomCamera: Cam_Entrance进门走廊
RoomCamera: Cam_ArenaBoss战固定锁定视野
CameraTriggerZone → Cam_Arena进入竞技场触发
IsBossRoom = true → GameManager 监听 EVT_BossFightStarted
```
---
## 六、编辑器工具
- **`BaseGames → Camera → Room Camera Setup`**:扫描当前场景所有 `RoomCamera` / `CameraTriggerZone`,显示绑定状态并提供一键修复。新建 Room 后务必用此工具检查是否存在未绑定的 Camera Zone。
- **`MapDatabaseSO.GetRoom(roomId)`**已内置懒加载字典索引O(1) 查找),无需手动维护索引。