# 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,再升高其优先级触发 Blend,Blend 结束后互换角色。 | 虚拟相机 | 默认 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 /// /// 挂载在子区域触发 Collider 上,玩家进入时激活指定 RoomCamera,离开时还原。 /// 可与 §3.2 RoomCamera 逻辑复用,SubRegionTrigger 本质是 CameraTriggerZone 的轻量别名。 /// [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` | | **双轴自由** | 大型开放场景、探索大厅 | 无 Lock,Confiner 覆盖整个区域 | | **双轴锁定(静止镜头)** | 剧场演出、特定剧情房间 | 使用固定位置 VCam,Priority 最高 | **锁轴时的边缘提示**:当房间宽度/高度接近相机视野的 1.5× 以内时,`RoomVisibleArea` 的 Gizmo 会在相应轴方向上绘制红色警告线,提示设计师考虑是否需要 `CameraAxisLock`。