chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

@@ -0,0 +1,643 @@
# 02 · 镜头系统
> **命名空间** `BaseGames.Camera`
> **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md)
> **依赖** Cinemachine 3 · `BaseGames.Core.Events`
---
## 目录
1. [设计目标](#1-设计目标)
2. [Cinemachine 3 核心组件](#2-cinemachine-3-核心组件)
3. [虚拟相机架构](#3-虚拟相机架构)
- 3.1 全局双机Global A/B
- 3.2 房间专用相机(可选)
- 3.3 特殊状态相机
4. [CameraStateController](#4-camerastatecontroller)
5. [可视区域与触发区域](#5-可视区域与触发区域)
- 5.1 可视区域RoomVisibleArea
- 5.2 切换触发区域CameraTriggerZone
6. [过渡效果CameraBlendProfileSO](#6-过渡效果camerablendprofileso)
7. [镜头震动CinemachineImpulse](#7-镜头震动cinemachineimpulse)
8. [像素对齐Pixel Perfect 集成](#8-像素对齐pixel-perfect-集成)
9. [CameraConfigSO — 配置资产](#9-cameraconfigso--配置资产)
10. [场景搭建规范](#10-场景搭建规范)
11. [编辑器友好设计](#11-编辑器友好设计)
12. [大房间相机处理](#12-大房间相机处理)
- 12.1 竖向/横向无缝滚动房间
- 12.2 子区域相机分段
- 12.3 滚动限制模式
---
## 1. 设计目标
- 镜头行为**完全由 Cinemachine 3 驱动**,不手动操作 `Camera.transform`
- **全局 A/B 双机交替复用**:切换房间时将 inactive 的全局相机预设好新边界再切过去,彻底消除 Confiner 跳变
- **房间专用相机(可选)**:每个房间可配置专属 `VCam_Room`,存在时自动优先使用;不配置则回退到全局双机
- **触发区域驱动切换**`CameraTriggerZone` Trigger Collider 控制镜头切换时机,可在 Scene 视图中可视化编辑
- **可视区域独立编辑**`RoomVisibleArea``PolygonCollider2D` 在 Scene 视图可直接拖拽顶点Gizmo 实时预览视野
- **过渡效果可配置**`CameraBlendProfileSO` 资产,可逐触发区域覆盖全局默认混合风格与时长
- 镜头震动通过 `CinemachineImpulseSource` 发布,与 Feel 的 `MMF_CinemachineImpulse` 集成
- 像素艺术风格保持亚像素锁定,防止渲染模糊
---
## 2. Cinemachine 3 核心组件
| 组件 | 挂载位置 | 说明 |
|------|---------|------|
| `CinemachineBrain` | Main Camera | 统一调度所有虚拟相机,同一时刻激活优先级最高者 |
| `CinemachineCamera` | 虚拟相机 GO | Cinemachine 3 的虚拟相机基础组件(取代旧版 `CinemachineVirtualCamera`|
| `CinemachinePositionComposer` | 虚拟相机组件 | 控制跟随目标偏移与阻尼(探索镜头用)|
| `CinemachineOrbitalFollow` | 虚拟相机组件 | Boss 战镜头围绕目标(可选)|
| `CinemachineConfiner2D` | 虚拟相机组件 | 房间边界约束(每个房间设置独立 `Collider2D`|
| `CinemachineImpulseSource` | 虚拟相机 GO | 发布震动冲量(与 Feel 集成)|
| `CinemachineImpulseListener` | 虚拟相机组件 | 接收并响应冲量(震屏强度系数)|
| `CinemachinePixelPerfect` | 虚拟相机组件 | 与 `PixelPerfectCamera` 协同,消除亚像素抖动 |
---
## 3. 虚拟相机架构
### 3.1 全局双机Global A/B
Persistent 场景中预置两台全局虚拟相机,同一时刻仅一台处于"热"优先级。切换房间时将 inactive 机预配置好新 Confiner再升高其优先级触发 BlendBlend 结束后互换角色。
| 虚拟相机 | 默认 Priority | 说明 |
|---------|:---:|------|
| `VCam_Global_A` | 10 | 初始活跃,跟踪玩家 |
| `VCam_Global_B` | 9 | 待机,切换时接管 |
**房间切换时序(无房间专用相机):**
```
玩家穿过 CameraTriggerZone
CameraStateController.SwitchRoom(data)
├─ 1. 更新 inactive 机(如 VCam_B
│ · BoundingShape2D ← 新房间 RoomVisibleArea.Collider2D
│ · FollowTarget ← 玩家 Transform不变
│ · PositionOffset ← data.cameraOffset
├─ 2. 将 data.blendProfile 写入 CinemachineBrain.DefaultBlend
├─ 3. inactiveCam.Priority = activeCam.Priority + 1
│ → CinemachineBrain 自动开始 Blend
└─ 4. Blend 结束CinemachineBrain.BlendFinished 回调)
activeCam.Priority = 9
swap _activeCam / _inactiveCam 引用
```
### 3.2 房间专用相机(可选)
每个房间场景可以放置一个带 `RoomCamera` 组件的虚拟相机。若存在,则优先级高于全局双机;全局双机继续在后台保持配置,以便专用相机卸载时无缝接管。
| 情况 | 使用相机 | Priority |
|------|---------|:---:|
| 房间无专用相机 | 全局 A/B 中的 active 方 | 10 |
| 房间有 `RoomCamera` | 房间专用虚拟相机 | 15 |
```csharp
// RoomCamera.cs — 挂在房间场景的 VCam_Room_XXX 上
public class RoomCamera : MonoBehaviour
{
[SerializeField] CinemachineCamera _vcam;
[SerializeField] RoomVisibleArea _visibleArea; // 本房间可视区域
[SerializeField] CameraBlendProfileSO _enterBlend; // 进入时的过渡(可留空)
void OnEnable() => CameraStateController.Instance.RegisterRoomCamera(this);
void OnDisable() => CameraStateController.Instance.UnregisterRoomCamera(this);
public CinemachineCamera Vcam => _vcam;
public RoomVisibleArea VisibleArea => _visibleArea;
public CameraBlendProfileSO EnterBlend => _enterBlend;
}
```
> 房间专用相机注册时,`CameraStateController` 同步将全局双机的 Confiner 也设置为同一区域,确保专用相机卸载后全局机能无缝接管。
### 3.3 特殊状态相机
以下相机覆盖所有普通相机,由 `CameraStateController` 响应事件频道激活:
| 虚拟相机 | Priority | 激活条件 | 默认 Blend |
|---------|:---:|---------|---------|
| `VCam_Boss` | 30 | `OnBossFightToggled.Raise(true)` | 0.8 s EaseIn |
| `VCam_Cutscene` | 40 | `OnCutsceneStarted` | 0.0 s Cut |
| `VCam_Death` | 50 | `OnPlayerDied` | 1.0 s EaseIn |
---
## 4. CameraStateController
`CameraStateController``MonoBehaviour`,挂在 CameraRig GO 上,`DefaultExecutionOrder(-200)`)负责维护全局双机状态、响应事件频道、协调房间专用相机:
```csharp
public class CameraStateController : MonoBehaviour
{
public static CameraStateController Instance { get; private set; }
[Header("全局双机")]
[SerializeField] CinemachineCamera _globalA;
[SerializeField] CinemachineCamera _globalB;
[Header("特殊状态机")]
[SerializeField] CinemachineCamera _vcamBoss;
[SerializeField] CinemachineCamera _vcamCutscene;
[SerializeField] CinemachineCamera _vcamDeath;
[Header("默认过渡效果")]
[SerializeField] CameraBlendProfileSO _defaultRoomBlend;
[Header("事件频道")]
[SerializeField] BoolEventChannelSO _onBossFightToggled;
[SerializeField] VoidEventChannelSO _onPlayerDied;
[SerializeField] VoidEventChannelSO _onCutsceneStarted;
[SerializeField] VoidEventChannelSO _onCutsceneEnded;
CinemachineCamera _activeCam; // 当前"热"全局机
CinemachineCamera _inactiveCam; // 待机全局机
RoomCamera _currentRoomCam;
// 切换到新房间(由 CameraTriggerZone 调用)
public void SwitchRoom(CameraTriggerZone.SwitchData data) { ... }
// 房间专用相机注册/反注册(由 RoomCamera.OnEnable/OnDisable 调用)
public void RegisterRoomCamera(RoomCamera cam) { ... }
public void UnregisterRoomCamera(RoomCamera cam) { ... }
}
```
**默认 Blend 查找顺序(优先级从高到低):**
```
触发区域 CameraTriggerZone._blendOverride (最高优先)
↓ 为空时
目标房间 RoomCamera._enterBlend
↓ 为空时
CameraStateController._defaultRoomBlend (全局兜底)
```
**特殊状态 Blend固定值不受 BlendProfile 影响):**
| 切换 | 时长 | 风格 | 说明 |
|------|------|------|------|
| 任意 → Boss | 0.8 s | EaseIn | 慢推入强调 Boss 出场 |
| Boss → 探索 | 0.5 s | EaseOut | 退场恢复 |
| 任意 → Death | 1.0 s | EaseIn | 慢推营造悲剧感 |
| 任意 → Cutscene | 0.0 s | Cut | 过场硬切 |
| Cutscene → 探索 | 0.3 s | EaseOut | 过场结束柔和恢复 |
---
## 5. 可视区域与触发区域
### 5.1 可视区域RoomVisibleArea
定义该房间允许相机移动的边界,驱动 `CinemachineConfiner2D`。每个房间场景(或同房间不同区段)放置一个:
```
Scene: Room_Forest_01
└── [VisibleArea_Main] (GameObject)
├── PolygonCollider2D (IsTrigger = false) ← Confiner 要求非 Trigger
└── RoomVisibleArea.cs
├── [SerializeField] string areaId // 唯一 ID"Room_Forest_01_Main"
├── [SerializeField] Vector2 cameraOffset // 此区域相机跟随偏移
└── [SerializeField] bool hideOnPlay // 运行时隐藏 Gizmo
```
> **重要**`CinemachineConfiner2D` 要求 `PolygonCollider2D.IsTrigger = false`。如需同一位置同时做触发检测,在子 GO 上另挂一个 Trigger Collider`CameraTriggerZone` 使用独立 GO
**边界约束参数**`CameraConfigSO`
| 参数 | 推荐值 | 说明 |
|------|:------:|------|
| `ConfinerDamping` | 0.0 | 到达边界时的阻尼0 = 硬停止)|
| `SlowingDistance` | 0.5 | 靠近边界时开始减速的距离 |
一个大房间可以配置**多个 `RoomVisibleArea`**(如上下层、分支路线),由不同的 `CameraTriggerZone` 分别引用。
---
### 5.2 切换触发区域CameraTriggerZone
`CameraTriggerZone` 是驱动相机切换的入口,通过 Trigger Collider 检测玩家进入:
```
Scene: Room_Forest_01
└── [CameraTrigger_Entry] (GameObject)
├── BoxCollider2D (IsTrigger = true) ← 放在入口门洞处,可视化编辑
└── CameraTriggerZone.cs
├── [SerializeField] RoomVisibleArea _targetVisibleArea // 切换到哪个可视区域
├── [SerializeField] RoomCamera _targetRoomCamera // 可留空(使用全局双机)
├── [SerializeField] CameraBlendProfileSO _blendOverride // 可留空(使用全局默认)
├── [SerializeField] Vector2 _cameraOffset // 可覆盖 VisibleArea 的偏移
└── [SerializeField] bool _triggerOnce // true = 仅触发一次
```
```csharp
public class CameraTriggerZone : MonoBehaviour
{
public struct SwitchData
{
public RoomVisibleArea visibleArea;
public RoomCamera roomCamera; // null = 使用全局双机
public CameraBlendProfileSO blendProfile; // null = 查找上级默认
public Vector2 cameraOffset;
}
void OnTriggerEnter2D(Collider2D other)
{
if (!other.CompareTag("Player")) return;
if (_triggerOnce && _triggered) return;
_triggered = true;
CameraStateController.Instance.SwitchRoom(new SwitchData
{
visibleArea = _targetVisibleArea,
roomCamera = _targetRoomCamera,
blendProfile = _blendOverride,
cameraOffset = _cameraOffset != Vector2.zero
? _cameraOffset
: _targetVisibleArea.CameraOffset
});
}
bool _triggered;
}
```
**典型放置方式**:在房间入口门洞处放置细长 `BoxCollider2D`(宽约 0.5 单位,高覆盖整个通道),确保玩家穿越时必然触发。`_triggerOnce = false` 时,玩家来回穿越可双向切换(适合同一大房间内的区段分割线)。
---
## 6. 过渡效果CameraBlendProfileSO
`CameraBlendProfileSO` 是一个轻量配置资产,封装 `CinemachineBlendDefinition`,可在 Inspector 中独立编辑并在多个触发区域之间复用:
```csharp
[CreateAssetMenu(menuName = "Camera/BlendProfile")]
public class CameraBlendProfileSO : ScriptableObject
{
[Tooltip("过渡风格Cut / EaseInOut / EaseIn / EaseOut / HardIn / HardOut / Linear")]
public CinemachineBlendDefinition.Styles style = CinemachineBlendDefinition.Styles.EaseInOut;
[Range(0f, 3f)]
public float duration = 0.4f;
public CinemachineBlendDefinition ToBlendDefinition()
=> new CinemachineBlendDefinition(style, duration);
}
```
**资产存放路径**`Assets/ScriptableObjects/Config/Camera/Blends/`
**预置资产(建议创建):**
| 资产名 | Style | Duration | 典型用途 |
|--------|-------|:--------:|----------|
| `Blend_Room_Default.asset` | EaseInOut | 0.4 s | 普通房间切换(全局兜底)|
| `Blend_Room_Snap.asset` | Cut | 0.0 s | 重生/传送等硬切 |
| `Blend_Room_Slow.asset` | EaseIn | 0.8 s | 进入特殊区域(如 Boss 前厅)|
| `Blend_Room_Fast.asset` | Linear | 0.15 s | 快速连续的密集小房间 |
---
## 7. 镜头震动CinemachineImpulse
### 震动类型设计
| 类型 | 触发来源 | 强度 | 时长 | 说明 |
|------|---------|------|------|------|
| `Light` | 玩家攻击命中普通敌人 | 0.2 | 0.1s | 轻微点击感 |
| `Medium` | 玩家受击 | 0.5 | 0.25s | 明显震动 |
| `Heavy` | 玩家死亡 / Boss 重击 | 1.0 | 0.4s | 强烈震动 |
| `Parry` | 弹反成功 | 0.7 | 0.2s | 带方向性(从敌人方向弹开)|
| `Landing` | 高空落地 | 0.3 | 0.15s | 垂直向下震动 |
### 震动集成方式
Feel 的 `MMF_CinemachineImpulse` Feedback 内部持有 `CinemachineImpulseSource`
- 命中时由 `PlayerFeedback` / `EnemyFeedback` 中的 `MMF_Player` 触发
- `CinemachineImpulseListener`(挂在虚拟相机上)响应冲量,配置强度缩放系数
**CinemachineImpulseSource 配置**Inspector
| 参数 | 推荐值 | 说明 |
|------|--------|------|
| `ImpulseDefinition.ImpulseType` | `Uniform` | 各方向均匀(普通)/ `Directional`(带方向性震动)|
| `ImpulseDefinition.ImpulseShape` | 自定义曲线 | 快速衰减曲线(前 20% 满强度,后 80% 指数衰减)|
| `DefaultVelocity` | (0.5, -0.5, 0) | 默认震动方向(右斜下)|
---
## 8. 像素对齐Pixel Perfect 集成
像素艺术游戏要求相机位置锁定到整数像素,否则 Sprite 会出现模糊/闪烁:
### 组件配置链
```
Main Camera
├── PixelPerfectCamera (com.unity.2d.pixel-perfect)
│ ├── Assets Pixels Per Unit: 32
│ ├── Reference Resolution: 480 × 270 (16:9 基准)
│ ├── Crop Frame X/Y: 关闭(允许缩放而非裁剪)
│ └── Upscale Render Texture: 开启(低分辨率渲染再放大)
└── CinemachineBrain
└── Update Method: Fixed Update与物理帧同步消除抖动
```
### 虚拟相机配置
每个虚拟相机上添加 `CinemachinePixelPerfect` Extension
- 此 Extension 自动将相机位置对齐到像素网格
-`PixelPerfectCamera` 配合,确保最终渲染无亚像素误差
### Orthographic Size 计算
$$\text{Orthographic Size} = \frac{\text{Reference Resolution Height}}{2 \times \text{PPU}} = \frac{270}{2 \times 32} = 4.21875$$
> 使用 `PixelPerfectCamera` 时Orthographic Size 由组件自动管理,勿手动设置。
---
## 9. CameraConfigSO — 配置资产
`CameraConfigSO` 集中管理所有可调节的镜头参数,存放于:
`Assets/ScriptableObjects/Config/Camera/CameraConfigSO.asset`
### 探索镜头参数
| 参数 | 类型 | 推荐值 | 说明 |
|------|------|--------|------|
| `ExploreFollowDamping` | `Vector2` | (0.2, 0.2) | XY 轴跟随阻尼 |
| `ExploreAheadDistance` | `float` | 2.0 | 朝移动方向的前瞻偏移量 |
| `ExploreAheadSmoothing` | `float` | 0.5 | 前瞻平滑时间 |
| `ExploreLookUpOffset` | `float` | 1.5 | 长按上看时的额外 Y 偏移 |
| `ExploreLookDownOffset` | `float` | -1.0 | 长按下看时的额外 Y 偏移 |
| `ExploreFOV` | `float` | 4.22 | Orthographic Size像素对齐|
### 战斗镜头参数
| 参数 | 类型 | 推荐值 | 说明 |
|------|------|--------|------|
| `CombatZoomOut` | `float` | +0.5 | 战斗时 Orthographic Size 增量(拉远)|
| `CombatForwardOffset` | `float` | 1.0 | 朝敌人方向额外偏移 |
| `CombatFollowDamping` | `Vector2` | (0.3, 0.3) | 战斗时更高阻尼(稳定)|
### Boss 镜头参数
| 参数 | 类型 | 推荐值 | 说明 |
|------|------|--------|------|
| `BossRoomCenter` | `Vector2` | 由 Boss 房间配置 | Boss 房间固定镜头中心 |
| `BossZoomLevel` | `float` | 6.0 | Boss 战 Orthographic Size |
---
## 10. 场景搭建规范
### Persistent 场景CameraRig 层级结构
```
[CameraRig] (Prefab — DontDestroyOnLoad放入 Persistent 场景)
├── Main Camera
│ ├── PixelPerfectCamera
│ └── CinemachineBrain
│ └── DefaultBlend ← 运行时由 CameraStateController 动态写入
├── [GlobalCameras] ← 全局双机,永远存在
│ ├── VCam_Global_A (CinemachineCamera, Priority 10)
│ │ ├── CinemachinePositionComposer (Follow = Player)
│ │ ├── CinemachineConfiner2D ← BoundingShape2D 运行时设置
│ │ ├── CinemachineImpulseListener
│ │ └── CinemachinePixelPerfect
│ └── VCam_Global_B (CinemachineCamera, Priority 9)
│ ├── CinemachinePositionComposer (Follow = Player)
│ ├── CinemachineConfiner2D
│ ├── CinemachineImpulseListener
│ └── CinemachinePixelPerfect
├── [SpecialCameras] ← 特殊状态机,默认 Priority 0
│ ├── VCam_Boss (CinemachineCamera)
│ │ ├── CinemachineConfiner2D ← Boss 房间专属 Confiner运行时设置
│ │ └── CinemachinePixelPerfect
│ ├── VCam_Death (CinemachineCamera)
│ │ └── CinemachinePixelPerfect
│ └── VCam_Cutscene (CinemachineCamera)
│ └── CinemachinePixelPerfect
├── [ImpulseSources]
│ ├── ImpulseSource_Light
│ ├── ImpulseSource_Medium
│ ├── ImpulseSource_Heavy
│ ├── ImpulseSource_Parry
│ └── ImpulseSource_Landing
└── CameraStateController.cs
```
### 房间场景中的标准配置
```
Room_XXX (Scene)
├── [VisibleArea_Main] (GameObject) ← 必须,定义主可视区域
│ ├── PolygonCollider2D (IsTrigger = false)
│ └── RoomVisibleArea.cs
│ └── areaId = "Room_XXX_Main"
├── [VisibleArea_Sub] (GameObject) ← 可选,房间有多段时使用
│ ├── PolygonCollider2D (IsTrigger = false)
│ └── RoomVisibleArea.cs
├── [CameraTrigger_Entry] (GameObject) ← 房间入口触发器
│ ├── BoxCollider2D (IsTrigger = true) ← 放在入口门洞处
│ └── CameraTriggerZone.cs
│ ├── _targetVisibleArea = VisibleArea_Main
│ ├── _targetRoomCamera = (留空→使用全局双机)
│ └── _blendOverride = (留空→全局默认)
└── [VCam_Room_XXX] (GameObject) ← 可选,房间专用相机
├── CinemachineCamera (Priority 15)
│ ├── CinemachinePositionComposer
│ ├── CinemachineConfiner2D
│ └── CinemachinePixelPerfect
└── RoomCamera.cs
├── _visibleArea = VisibleArea_Main
└── _enterBlend = Blend_Room_Slow (可选)
```
---
## 11. 编辑器友好设计
### RoomVisibleArea — Scene 视图
- `PolygonCollider2D` 内置**Edit Collider** 按钮,可直接在 Scene 视图拖拽顶点编辑边界形状
- `RoomVisibleArea` 始终绘制 **青色轮廓线 + 半透明青色填充**,表示 Confiner 边界
- 叠加绘制 **白色虚线矩形** 表示当前 Orthographic Size 对应的实际视野范围,方便确认边界是否足够
- Inspector 中自动检测:若 PolygonCollider2D 包围盒小于摄像机视野,显示红色警告:
`⚠ 可视区域小于摄像机视野,镜头将无法移动!`
### CameraTriggerZone — Scene 视图
- 触发区域 Collider2D 始终绘制 **黄色轮廓**(即使未选中 GO
- 选中时:绘制从触发区域中心指向 `_targetVisibleArea` 中心的 **橙色箭头**,直观显示切换目标
-`_targetRoomCamera` 不为空,额外绘制 **紫色细线** 指向专用相机位置
- Inspector 中 `_blendOverride` 字段旁显示当前生效的 Blend 描述(含回退链说明):
```
生效 Blend: [触发区域覆盖] EaseIn 0.8s
生效 Blend: [全局兜底] EaseInOut 0.4s
```
### CameraStateController — 运行时监控
Play Mode 下 Inspector 显示:
```
┌─ CameraStateController ──────────────────────────────────────────┐
│ Active Global : VCam_Global_A (Priority 10) │
│ Inactive Global: VCam_Global_B (Priority 9) │
│ Room Camera : VCam_Room_Forest01 (Priority 15) │
│ Blend Progress : ████████████░░░░ 75% │
│ Current Area : Room_Forest_01_Main │
│ ─────────────────────────────────────────────────────────────── │
│ [触发房间切换] [触发 Boss] [触发死亡] [强制 Snap] │
└──────────────────────────────────────────────────────────────────┘
```
### Scene 视图 Gizmos 汇总
| 组件 | Gizmo 颜色 | 内容 |
|------|-----------|------|
| `RoomVisibleArea` | 青色 | Confiner 边界轮廓 + 当前视野矩形 |
| `CameraTriggerZone` | 黄色 | 触发 Collider 轮廓 |
| `CameraTriggerZone`(选中) | 橙色箭头 | 指向目标 VisibleArea |
| `RoomCamera` | 紫色 | VCam 视锥Frustum轮廓 |
| `CinemachineImpulseSource` | 红色箭头 | 震动方向,长度表示强度 |
| `LargeRoomBoundary` | 绿色粗线框 | 大房间整体边界 |
| `SubRegionTrigger`(选中) | 青绿箭头 | 指向该子区域激活的 VCam |
---
## 12. 大房间相机处理
银河恶魔城中存在若干**大型房间**Boss 房、竖井、长廊),单个 Confiner 配合全局相机会导致视野移动范围过大、失去房间感。本节说明三种处理策略。
### 12.1 竖向/横向无缝滚动房间
**适用**长廊横向滚动、竖井纵向滚动、Boss 前庭。
做法:创建宽/高尺寸匹配房间的 `RoomVisibleArea`PolygonCollider2D全局相机的 Confiner 直接限制在这块大 Confiner 里自由滑动。
```
房间宽度 60 单位、高度 12 单位(横向长廊):
RoomVisibleArea:
PolygonCollider2D 顶点: (-30,-6), (30,-6), (30,6), (-30,6)
全局相机 Confiner2D → 绑定这个 Collider
结果: 镜头可以横向自由跟随,纵向锁死在房间高度内
```
**锁轴配置**(在 `RoomVisibleArea` 上附加 `CameraAxisLock`
```csharp
public class CameraAxisLock : MonoBehaviour
{
public enum LockMode { None, LockX, LockY }
[SerializeField] LockMode _lock = LockMode.None;
[SerializeField] float _lockedValue; // LockX 时为固定 X 坐标LockY 时为固定 Y 坐标
// CameraStateController 在读取 RoomVisibleArea 时检查是否挂载了此组件
// 若存在,则在 LateUpdate 中强制修正相机位置
public void ApplyLock(CinemachineBrain brain)
{
var pos = brain.transform.position;
switch (_lock)
{
case LockMode.LockX: brain.transform.position = new Vector3(_lockedValue, pos.y, pos.z); break;
case LockMode.LockY: brain.transform.position = new Vector3(pos.x, _lockedValue, pos.z); break;
}
}
}
```
### 12.2 子区域相机分段
**适用**Boss 房(进入 Boss 房后锁定在 Boss 战视角)、多功能大厅(不同区域有不同镜头高度)。
策略:在大房间内放置若干 `SubRegionTrigger`,每个子区域激活专属 `RoomCamera`(更高 Priority
```
大房间层级:
├── LargeRoom_BossArena
│ ├── RoomVisibleArea_Global ← 整体边界(兜底 Confiner
│ ├── SubRegionTrigger_Entrance ← 入口区,激活 VCam_Entrance低焦
│ ├── SubRegionTrigger_Arena ← 战斗区,激活 VCam_Arena剧院视角
│ └── SubRegionTrigger_Exit ← 出口区,激活 VCam_Exit低焦
```
```csharp
/// <summary>
/// 挂载在子区域触发 Collider 上,玩家进入时激活指定 RoomCamera离开时还原。
/// 可与 §3.2 RoomCamera 逻辑复用SubRegionTrigger 本质是 CameraTriggerZone 的轻量别名。
/// </summary>
[RequireComponent(typeof(Collider2D))]
public class SubRegionTrigger : MonoBehaviour
{
[SerializeField] CinemachineCamera _subCamera; // 子区域专用 VCam
[SerializeField] CameraBlendProfileSO _blendIn; // 进入时的 Blend
[SerializeField] CameraBlendProfileSO _blendOut; // 离开时的 Blend可为 null用全局兜底
void OnTriggerEnter2D(Collider2D other)
{
if (!other.CompareTag("Player")) return;
CameraStateController.Instance.ActivateSubRegion(_subCamera, _blendIn);
}
void OnTriggerExit2D(Collider2D other)
{
if (!other.CompareTag("Player")) return;
CameraStateController.Instance.DeactivateSubRegion(_subCamera, _blendOut);
}
}
```
`CameraStateController` 新增方法:
```csharp
public void ActivateSubRegion(CinemachineCamera subCam, CameraBlendProfileSO blend)
{
if (blend != null)
ApplyBlendOverride(subCam, blend);
subCam.Priority = _roomCameraBasePriority + 5; // 高于普通房间相机
}
public void DeactivateSubRegion(CinemachineCamera subCam, CameraBlendProfileSO blend)
{
if (blend != null)
ApplyBlendOverride(subCam, blend);
subCam.Priority = 0;
}
```
### 12.3 滚动限制模式
针对特定大型场景需要限制某轴的相机运动:
| 模式 | 典型场景 | 配置 |
|------|---------|------|
| **横向自由 + 纵轴锁定** | 横向长廊、横向 Boss 房 | `CameraAxisLock.LockY` |
| **纵向自由 + 横轴锁定** | 竖井、电梯通道 | `CameraAxisLock.LockX` |
| **双轴自由** | 大型开放场景、探索大厅 | 无 LockConfiner 覆盖整个区域 |
| **双轴锁定(静止镜头)** | 剧场演出、特定剧情房间 | 使用固定位置 VCamPriority 最高 |
**锁轴时的边缘提示**:当房间宽度/高度接近相机视野的 1.5× 以内时,`RoomVisibleArea` 的 Gizmo 会在相应轴方向上绘制红色警告线,提示设计师考虑是否需要 `CameraAxisLock`。