chore: initial commit
This commit is contained in:
643
Docs/Design/02_CameraSystem.md
Normal file
643
Docs/Design/02_CameraSystem.md
Normal 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,再升高其优先级触发 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
|
||||
/// <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` |
|
||||
| **双轴自由** | 大型开放场景、探索大厅 | 无 Lock,Confiner 覆盖整个区域 |
|
||||
| **双轴锁定(静止镜头)** | 剧场演出、特定剧情房间 | 使用固定位置 VCam,Priority 最高 |
|
||||
|
||||
**锁轴时的边缘提示**:当房间宽度/高度接近相机视野的 1.5× 以内时,`RoomVisibleArea` 的 Gizmo 会在相应轴方向上绘制红色警告线,提示设计师考虑是否需要 `CameraAxisLock`。
|
||||
Reference in New Issue
Block a user