Files
zeling_v2/Docs/Design/02_CameraSystem.md
2026-05-08 11:04:00 +08:00

26 KiB
Raw Permalink Blame History

02 · 镜头系统

命名空间 BaseGames.Camera
所属文档集 ← 返回索引 · 总览
依赖 Cinemachine 3 · BaseGames.Core.Events


目录

  1. 设计目标
  2. Cinemachine 3 核心组件
  3. 虚拟相机架构
    • 3.1 全局双机Global A/B
    • 3.2 房间专用相机(可选)
    • 3.3 特殊状态相机
  4. CameraStateController
  5. 可视区域与触发区域
    • 5.1 可视区域RoomVisibleArea
    • 5.2 切换触发区域CameraTriggerZone
  6. 过渡效果CameraBlendProfileSO
  7. 镜头震动CinemachineImpulse
  8. 像素对齐Pixel Perfect 集成
  9. CameraConfigSO — 配置资产
  10. 场景搭建规范
  11. 编辑器友好设计
  12. 大房间相机处理
    • 12.1 竖向/横向无缝滚动房间
    • 12.2 子区域相机分段
    • 12.3 滚动限制模式

1. 设计目标

  • 镜头行为完全由 Cinemachine 3 驱动,不手动操作 Camera.transform
  • 全局 A/B 双机交替复用:切换房间时将 inactive 的全局相机预设好新边界再切过去,彻底消除 Confiner 跳变
  • 房间专用相机(可选):每个房间可配置专属 VCam_Room,存在时自动优先使用;不配置则回退到全局双机
  • 触发区域驱动切换CameraTriggerZone Trigger Collider 控制镜头切换时机,可在 Scene 视图中可视化编辑
  • 可视区域独立编辑RoomVisibleAreaPolygonCollider2D 在 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
// 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

CameraStateControllerMonoBehaviour,挂在 CameraRig GO 上,DefaultExecutionOrder(-200))负责维护全局双机状态、响应事件频道、协调房间专用相机:

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 ColliderCameraTriggerZone 使用独立 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 = 仅触发一次
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 中独立编辑并在多个触发区域之间复用:

[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

使用 PixelPerfectCameraOrthographic 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 前庭。

做法:创建宽/高尺寸匹配房间的 RoomVisibleAreaPolygonCollider2D全局相机的 Confiner 直接限制在这块大 Confiner 里自由滑动。

房间宽度 60 单位、高度 12 单位(横向长廊):
  RoomVisibleArea:
    PolygonCollider2D 顶点: (-30,-6), (30,-6), (30,6), (-30,6)
  全局相机 Confiner2D → 绑定这个 Collider
  结果: 镜头可以横向自由跟随,纵向锁死在房间高度内

锁轴配置(在 RoomVisibleArea 上附加 CameraAxisLock

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低焦
/// <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 新增方法:

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