摄像机区域的优化
This commit is contained in:
109
Assets/_Game/Scripts/Editor/Scene/PersistentSceneAutoLoader.cs
Normal file
109
Assets/_Game/Scripts/Editor/Scene/PersistentSceneAutoLoader.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace BaseGames.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 编辑器 Edit Mode 辅助:打开任意场景时自动将 Persistent 场景 Additive 加入 Hierarchy。
|
||||
///
|
||||
/// 职责范围(仅限 Edit Mode):
|
||||
/// 让设计师在编辑房间场景时,Inspector 中可直接看到并配置 GameManager / SceneService
|
||||
/// 等 Persistent 场景内的组件,无需手动 Open Additive。
|
||||
///
|
||||
/// 运行时(Play Mode / 发行版 Build)的保证由 GameBootstrap(Runtime 程序集)负责,
|
||||
/// 本脚本与 Play Mode 状态无关,不监听 playModeStateChanged。
|
||||
///
|
||||
/// 菜单:BaseGames/Tools/Edit Mode: Auto-Open Persistent Scene
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
public static class PersistentSceneAutoLoader
|
||||
{
|
||||
// ── 常量 ─────────────────────────────────────────────────────────────
|
||||
private const string MenuPath = "BaseGames/Tools/Edit Mode: Auto-Open Persistent Scene";
|
||||
private const string PrefKey = "BaseGames_EditAutoOpen_Persistent";
|
||||
private const string PersistentSceneName = "Scene_Persistent";
|
||||
|
||||
// ── 构造(Editor 启动时执行)──────────────────────────────────────────
|
||||
static PersistentSceneAutoLoader()
|
||||
{
|
||||
EditorSceneManager.sceneOpened += OnSceneOpened;
|
||||
|
||||
// 启动时补一次检查:Editor 已打开但 Persistent 不在 Hierarchy 的场景
|
||||
EditorApplication.delayCall += EnsurePersistentInHierarchyEditMode;
|
||||
}
|
||||
|
||||
// ── 菜单 ─────────────────────────────────────────────────────────────
|
||||
[MenuItem(MenuPath, validate = false, priority = 301)]
|
||||
private static void ToggleEnabled()
|
||||
{
|
||||
bool current = EditorPrefs.GetBool(PrefKey, true);
|
||||
EditorPrefs.SetBool(PrefKey, !current);
|
||||
}
|
||||
|
||||
[MenuItem(MenuPath, validate = true)]
|
||||
private static bool ToggleEnabledValidate()
|
||||
{
|
||||
Menu.SetChecked(MenuPath, EditorPrefs.GetBool(PrefKey, true));
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── 场景打开回调 ──────────────────────────────────────────────────────
|
||||
private static void OnSceneOpened(Scene scene, OpenSceneMode mode)
|
||||
{
|
||||
// 若是 Persistent 本身被打开,无需额外处理
|
||||
if (IsPersistentScene(scene.name)) return;
|
||||
|
||||
// Single 模式(替换当前场景)或 Additive 加入新场景时,确保 Persistent 也在 Hierarchy 中
|
||||
// 使用 delayCall 避免在场景加载中途调用 OpenScene
|
||||
EditorApplication.delayCall += EnsurePersistentInHierarchyEditMode;
|
||||
}
|
||||
|
||||
// ── 核心逻辑 ──────────────────────────────────────────────────────────
|
||||
private static void EnsurePersistentInHierarchyEditMode()
|
||||
{
|
||||
// 仅在 Edit Mode 执行(Play Mode 由 GameBootstrap 负责)
|
||||
if (Application.isPlaying) return;
|
||||
if (!EditorPrefs.GetBool(PrefKey, true)) return;
|
||||
|
||||
// 若 Persistent 已在 Hierarchy,无需操作
|
||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||
if (IsPersistentScene(SceneManager.GetSceneAt(i).name)) return;
|
||||
|
||||
// 查找并 Additive 打开 Persistent 场景
|
||||
string path = FindPersistentScenePath();
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"[PersistentAutoLoader] 未找到 '{PersistentSceneName}' 场景。" +
|
||||
"请确认场景已添加到 Build Settings 或可在 Assets 中搜索到。");
|
||||
return;
|
||||
}
|
||||
|
||||
EditorSceneManager.OpenScene(path, OpenSceneMode.Additive);
|
||||
}
|
||||
|
||||
// ── 工具函数 ──────────────────────────────────────────────────────────
|
||||
private static bool IsPersistentScene(string sceneName)
|
||||
=> sceneName == PersistentSceneName || sceneName == "Persistent";
|
||||
|
||||
private static string FindPersistentScenePath()
|
||||
{
|
||||
// 优先从 Build Settings 查找(保证与 GameBootstrap 使用同一文件)
|
||||
foreach (var buildScene in EditorBuildSettings.scenes)
|
||||
{
|
||||
if (!buildScene.enabled) continue;
|
||||
string name = System.IO.Path.GetFileNameWithoutExtension(buildScene.path);
|
||||
if (IsPersistentScene(name)) return buildScene.path;
|
||||
}
|
||||
|
||||
// 回退:在 AssetDatabase 中搜索
|
||||
string[] guids = AssetDatabase.FindAssets($"t:Scene {PersistentSceneName}");
|
||||
if (guids.Length > 0) return AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
|
||||
guids = AssetDatabase.FindAssets("t:Scene Persistent");
|
||||
return guids.Length > 0 ? AssetDatabase.GUIDToAssetPath(guids[0]) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b5ab9e5f153fb148817239307245e00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -385,46 +385,101 @@ namespace BaseGames.Editor
|
||||
MarkDirtyAndLog("Room Transition", go, report);
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Scene/Place/Room Camera", priority = 140)]
|
||||
public static void PlaceRoomCamera()
|
||||
[MenuItem("BaseGames/Scene/Place/Camera Area", priority = 140)]
|
||||
public static void PlaceCameraArea() => PlaceCameraArea("CameraArea");
|
||||
|
||||
/// <param name="areaName">
|
||||
/// 生成的 CameraArea GameObject 名称。
|
||||
/// 子节点 AreaBoundary 和 TriggerZone 将以此为前缀命名(如 MyZone_AreaBoundary)。
|
||||
/// </param>
|
||||
/// <param name="parent">生成的 GameObject 所挂载的父节点(为 null 时放置于场景根节点)。</param>
|
||||
public static void PlaceCameraArea(string areaName, Transform parent = null)
|
||||
{
|
||||
var report = new List<string>();
|
||||
int undoGroup = Undo.GetCurrentGroup();
|
||||
Undo.SetCurrentGroupName("Place Camera Area (+ TriggerZone)");
|
||||
|
||||
GameObject go = new GameObject("RoomCamera");
|
||||
Undo.RegisterCreatedObjectUndo(go, "Place Room Camera");
|
||||
go.transform.position = GetDropPosition();
|
||||
Vector3 pos = GetDropPosition();
|
||||
|
||||
CinemachineCamera cinemachine = GetOrAddComponent<CinemachineCamera>(go);
|
||||
RoomCamera roomCamera = GetOrAddComponent<RoomCamera>(go);
|
||||
CinemachineConfiner2D confiner = GetOrAddComponent<CinemachineConfiner2D>(go);
|
||||
// ── CameraArea ─────────────────────────────────────────────────────
|
||||
GameObject go = new GameObject(areaName);
|
||||
Undo.RegisterCreatedObjectUndo(go, "Place Camera Area");
|
||||
go.transform.position = pos;
|
||||
if (parent != null)
|
||||
Undo.SetTransformParent(go.transform, parent, "Parent Camera Area");
|
||||
|
||||
// RoomBoundary child — defines the camera confinement area
|
||||
Transform boundaryT = GetOrCreateChild(go.transform, "RoomBoundary");
|
||||
CameraArea cameraArea = GetOrAddComponent<CameraArea>(go);
|
||||
|
||||
// AreaBoundary child — 提供 CinemachineConfiner2D 所需的限位多边形(isTrigger = true,仅作为相机约束边界)
|
||||
Transform boundaryT = GetOrCreateChild(go.transform, $"{areaName}_AreaBoundary");
|
||||
PolygonCollider2D boundaryCollider = GetOrAddComponent<PolygonCollider2D>(boundaryT.gameObject);
|
||||
boundaryCollider.isTrigger = true;
|
||||
boundaryCollider.pathCount = 1;
|
||||
// 顶点必须逆时针(CCW)排列:Cinemachine 底层 Clipper 库对 CW 多边形(area<0)会取反 delta,
|
||||
// 导致向外膨胀而非向内收缩,相机将不受限制地跑出边界。
|
||||
boundaryCollider.SetPath(0, new Vector2[]
|
||||
{
|
||||
new Vector2(-12f, -6f),
|
||||
new Vector2(-12f, 6f),
|
||||
new Vector2( 12f, 6f),
|
||||
new Vector2( 12f, -6f),
|
||||
new Vector2(-12f, -6f), // BL
|
||||
new Vector2( 12f, -6f), // BR
|
||||
new Vector2( 12f, 6f), // TR
|
||||
new Vector2(-12f, 6f), // TL
|
||||
});
|
||||
RoomVisibleArea visibleArea = GetOrAddComponent<RoomVisibleArea>(boundaryT.gameObject);
|
||||
|
||||
AssignReference(roomCamera, "_visibleArea", visibleArea, report);
|
||||
AssignReference(confiner, "m_BoundingShape2D", boundaryCollider, report);
|
||||
AssignReference(cameraArea, "_confinerCollider", boundaryCollider, report);
|
||||
|
||||
// Disable any Camera and AudioListener added by Cinemachine
|
||||
UnityEngine.Camera cam = go.GetComponent<UnityEngine.Camera>();
|
||||
if (cam != null) cam.enabled = false;
|
||||
AudioListener al = go.GetComponent<AudioListener>();
|
||||
if (al != null) { Undo.DestroyObjectImmediate(al); }
|
||||
// ── CameraTriggerZone(配对)─────────────────────────────────────────
|
||||
GameObject zoneGo = new GameObject($"{areaName}_TriggerZone");
|
||||
Undo.RegisterCreatedObjectUndo(zoneGo, "Place Camera Trigger Zone");
|
||||
zoneGo.transform.position = pos;
|
||||
SetLayer(zoneGo, "TriggerZone", report);
|
||||
|
||||
report.Add("将 Player/CameraFollowTarget Transform 拖入 CinemachineCamera.Follow 字段以跟随玩家(或使用 Room Camera Setup 工具批量赋值)。");
|
||||
report.Add("调整 RoomBoundary PolygonCollider2D 顶点以匹配房间边界。");
|
||||
PolygonCollider2D col = GetOrAddComponent<PolygonCollider2D>(zoneGo);
|
||||
col.isTrigger = true;
|
||||
// 默认矩形轮廓(CCW),与 AreaBoundary 默认尺寸一致(可在 Inspector 中编辑顶点调整为任意多边形)
|
||||
col.SetPath(0, new Vector2[]
|
||||
{
|
||||
new Vector2(-12f, -6f), // BL
|
||||
new Vector2( 12f, -6f), // BR
|
||||
new Vector2( 12f, 6f), // TR
|
||||
new Vector2(-12f, 6f), // TL
|
||||
});
|
||||
|
||||
CameraTriggerZone zone = GetOrAddComponent<CameraTriggerZone>(zoneGo);
|
||||
AssignReference(zone, "_targetArea", cameraArea, report);
|
||||
// TriggerZone 归入 CameraArea 节点,方便统一调整与查找
|
||||
Undo.SetTransformParent(zoneGo.transform, go.transform, "Parent TriggerZone to CameraArea");
|
||||
zoneGo.transform.localPosition = Vector3.zero;
|
||||
Undo.CollapseUndoOperations(undoGroup);
|
||||
|
||||
report.Add($"调整 {areaName}_AreaBoundary PolygonCollider2D 顶点以匹配区域边界。");
|
||||
report.Add($"调整 {areaName}_TriggerZone PolygonCollider2D 顶点以匹配入口走廊(支持任意多边形)。");
|
||||
|
||||
// ── 自动关联到同场景 RoomController(若其 _cameraArea 为空)────────
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
var roomControllers = Object.FindObjectsByType<RoomController>(FindObjectsSortMode.None);
|
||||
#else
|
||||
var roomControllers = Object.FindObjectsOfType<RoomController>();
|
||||
#endif
|
||||
bool autoAssigned = false;
|
||||
foreach (var rc in roomControllers)
|
||||
{
|
||||
// 仅使用反射检查,避免每次都覆盖已绑定的引用
|
||||
var fi = typeof(RoomController).GetField("_cameraArea",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
if (fi == null) continue;
|
||||
if (fi.GetValue(rc) != null) continue;
|
||||
|
||||
Undo.RecordObject(rc, "Auto-assign CameraArea to RoomController");
|
||||
fi.SetValue(rc, cameraArea);
|
||||
EditorUtility.SetDirty(rc);
|
||||
report.Add($"✅ 已自动将 {areaName} 关联到 {rc.gameObject.name}.RoomController._cameraArea。");
|
||||
autoAssigned = true;
|
||||
}
|
||||
if (!autoAssigned)
|
||||
report.Add("将此 CameraArea 拖入 RoomController._cameraArea 字段(未找到空 _cameraArea 的 RoomController)。");
|
||||
|
||||
Selection.activeGameObject = go;
|
||||
MarkDirtyAndLog("Room Camera", go, report);
|
||||
MarkDirtyAndLog($"Camera Area (+ TriggerZone): {areaName}", go, report);
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Scene/Place/Ground Platform", priority = 150)]
|
||||
@@ -534,28 +589,6 @@ namespace BaseGames.Editor
|
||||
MarkDirtyAndLog("Nav Surface", go, report);
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Scene/Place/Camera Trigger Zone", priority = 180)]
|
||||
public static void PlaceCameraTriggerZone()
|
||||
{
|
||||
var report = new List<string>();
|
||||
|
||||
GameObject go = new GameObject("CameraTriggerZone");
|
||||
Undo.RegisterCreatedObjectUndo(go, "Place Camera Trigger Zone");
|
||||
go.transform.position = GetDropPosition();
|
||||
SetLayer(go, "TriggerZone", report);
|
||||
|
||||
BoxCollider2D col = GetOrAddComponent<BoxCollider2D>(go);
|
||||
col.isTrigger = true;
|
||||
col.size = new Vector2(2f, 2f);
|
||||
|
||||
GetOrAddComponent<CameraTriggerZone>(go);
|
||||
|
||||
report.Add("将目标 RoomCamera 拖入 CameraTriggerZone._targetCamera 字段。");
|
||||
|
||||
Selection.activeGameObject = go;
|
||||
MarkDirtyAndLog("Camera Trigger Zone", go, report);
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Scene/Place/Obstacle (Static)", priority = 190)]
|
||||
public static void PlaceObstacle()
|
||||
{
|
||||
|
||||
@@ -86,6 +86,29 @@ namespace BaseGames.Editor
|
||||
CameraStateController cameraStateController = GetOrAddComponent<CameraStateController>(cameraStateGo);
|
||||
CinemachineImpulseSource impulseSource = GetOrAddComponent<CinemachineImpulseSource>(cameraStateGo);
|
||||
|
||||
// 垂直窥视系统:独立节点,CameraStateController 持引用
|
||||
GameObject lookSystemGo = GetOrCreateChild(camera, "CameraLookSystem").gameObject;
|
||||
CameraLookSystem lookSystem = GetOrAddComponent<CameraLookSystem>(lookSystemGo);
|
||||
|
||||
GameObject vcamAGo = GetOrCreateChild(camera, "VCamA").gameObject;
|
||||
CinemachineCamera vcamA = GetOrAddComponent<CinemachineCamera>(vcamAGo);
|
||||
GetOrAddComponent<CinemachineConfiner2D>(vcamAGo);
|
||||
GetOrAddComponent<CameraAxisLockExtension>(vcamAGo);
|
||||
GetOrAddComponent<CameraAsymmetricDampingExtension>(vcamAGo);
|
||||
GetOrAddComponent<CameraAdaptiveLookaheadExtension>(vcamAGo);
|
||||
// CinemachinePositionComposer:Body 阶段组件,必须存在;ConfigureSlot 依赖它写入所有相机跟随参数
|
||||
var composerA = GetOrAddComponent<CinemachinePositionComposer>(vcamAGo);
|
||||
ApplyComposerDefaults(composerA);
|
||||
|
||||
GameObject vcamBGo = GetOrCreateChild(camera, "VCamB").gameObject;
|
||||
CinemachineCamera vcamB = GetOrAddComponent<CinemachineCamera>(vcamBGo);
|
||||
GetOrAddComponent<CinemachineConfiner2D>(vcamBGo);
|
||||
GetOrAddComponent<CameraAxisLockExtension>(vcamBGo);
|
||||
GetOrAddComponent<CameraAsymmetricDampingExtension>(vcamBGo);
|
||||
GetOrAddComponent<CameraAdaptiveLookaheadExtension>(vcamBGo);
|
||||
var composerB = GetOrAddComponent<CinemachinePositionComposer>(vcamBGo);
|
||||
ApplyComposerDefaults(composerB);
|
||||
|
||||
GameObject uiRootGo = GetOrCreateChild(ui, "UIRoot").gameObject;
|
||||
UIManager uiManager = GetOrAddComponent<UIManager>(uiRootGo);
|
||||
|
||||
@@ -146,6 +169,11 @@ namespace BaseGames.Editor
|
||||
|
||||
AssignReference(cameraStateController, "_brain", brain);
|
||||
AssignReference(cameraStateController, "_impulseSource", impulseSource);
|
||||
AssignReference(cameraStateController, "_lookSystem", lookSystem);
|
||||
AssignReference(cameraStateController, "_vcamA", vcamA);
|
||||
AssignReference(cameraStateController, "_vcamB", vcamB);
|
||||
AssignAsset(cameraStateController, "_onPlayerSpawned", report, true, "EVT_PlayerSpawned");
|
||||
AssignAsset(cameraStateController, "_lensConfig", report, false, "CAM_LensConfig", "LensConfig", "CameraLensConfig");
|
||||
|
||||
AssignReference(uiManager, "_hudRoot", hudRootGo);
|
||||
AssignReference(uiManager, "_pauseMenuRoot", pauseRootGo);
|
||||
@@ -189,13 +217,12 @@ namespace BaseGames.Editor
|
||||
// ── [Camera] ───────────────────────────────────────────────────
|
||||
Transform cameraGroup = GetOrCreateChild(root.transform, "[Camera]");
|
||||
|
||||
GameObject roomCameraGo = GetOrCreateChild(cameraGroup, "RoomCamera").gameObject;
|
||||
CinemachineCamera cinemachineCamera = GetOrAddComponent<CinemachineCamera>(roomCameraGo);
|
||||
RoomCamera roomCamera = GetOrAddComponent<RoomCamera>(roomCameraGo);
|
||||
CinemachineConfiner2D confiner = GetOrAddComponent<CinemachineConfiner2D>(roomCameraGo);
|
||||
// CameraArea — 定义相机区域(限位 + 混合配置 + 可选专有 VCam)
|
||||
GameObject cameraAreaGo = GetOrCreateChild(cameraGroup, "CameraArea").gameObject;
|
||||
CameraArea cameraArea = GetOrAddComponent<CameraArea>(cameraAreaGo);
|
||||
|
||||
// RoomBoundary — defines visible area and confiner polygon
|
||||
Transform boundaryT = GetOrCreateChild(roomCameraGo.transform, "RoomBoundary");
|
||||
// AreaBoundary — 提供 CinemachineConfiner2D 所需的限位多边形
|
||||
Transform boundaryT = GetOrCreateChild(cameraAreaGo.transform, "AreaBoundary");
|
||||
PolygonCollider2D boundaryCollider = GetOrAddComponent<PolygonCollider2D>(boundaryT.gameObject);
|
||||
boundaryCollider.pathCount = 1;
|
||||
boundaryCollider.SetPath(0, new Vector2[]
|
||||
@@ -203,16 +230,8 @@ namespace BaseGames.Editor
|
||||
new Vector2(-12f, -6f), new Vector2(-12f, 6f),
|
||||
new Vector2( 12f, 6f), new Vector2( 12f, -6f),
|
||||
});
|
||||
RoomVisibleArea visibleArea = GetOrAddComponent<RoomVisibleArea>(boundaryT.gameObject);
|
||||
|
||||
AssignReference(roomCamera, "_visibleArea", visibleArea);
|
||||
AssignReference(confiner, "m_BoundingShape2D", boundaryCollider);
|
||||
|
||||
// Disable stray Camera / AudioListener components sometimes added by Cinemachine
|
||||
UnityEngine.Camera staleCam = roomCameraGo.GetComponent<UnityEngine.Camera>();
|
||||
if (staleCam != null) staleCam.enabled = false;
|
||||
AudioListener staleAl = roomCameraGo.GetComponent<AudioListener>();
|
||||
if (staleAl != null) { Undo.DestroyObjectImmediate(staleAl); }
|
||||
AssignReference(cameraArea, "_confinerCollider", boundaryCollider);
|
||||
|
||||
// ── [SpawnPoints] ──────────────────────────────────────────────
|
||||
Transform spawnGroup = GetOrCreateChild(root.transform, "[SpawnPoints]");
|
||||
@@ -250,7 +269,7 @@ namespace BaseGames.Editor
|
||||
GetOrCreateChild(root.transform, "[Transitions]");
|
||||
|
||||
// ── Wire RoomController ────────────────────────────────────────
|
||||
AssignReference(roomController, "_roomCamera", roomCamera);
|
||||
AssignReference(roomController, "_cameraArea", cameraArea);
|
||||
|
||||
SerializedObject roomSO = new SerializedObject(roomController);
|
||||
SerializedProperty spawnArrayProp = roomSO.FindProperty("_spawnPoints");
|
||||
@@ -263,8 +282,7 @@ namespace BaseGames.Editor
|
||||
|
||||
// ── Report ─────────────────────────────────────────────────────
|
||||
report.Add("在 RoomController._roomId 填写唯一房间 ID(如 \"Room_Forest_01\")。");
|
||||
report.Add("将 Player/CameraFollowTarget Transform 拖入 CinemachineCamera.Follow 字段以跟随玩家(或使用 BaseGames → Camera → Room Camera Setup 工具批量赋值)。");
|
||||
report.Add("调整 RoomBoundary PolygonCollider2D 顶点以匹配实际房间大小。");
|
||||
report.Add("调整 AreaBoundary PolygonCollider2D 顶点以匹配实际房间大小。");
|
||||
report.Add("使用 Tile Palette 在 Ground Tilemap 上绘制地形,然后在 NavSurface Inspector 中点击 Bake。");
|
||||
report.Add("[Transitions] 子节点下使用 BaseGames/Scene/Place/Room Transition 添加过渡点。");
|
||||
|
||||
@@ -561,6 +579,34 @@ namespace BaseGames.Editor
|
||||
|
||||
Debug.LogWarning($"[SceneScaffoldTools] {scaffoldName} 完成,但仍有 {report.Count} 项需要手工确认:\n- {string.Join("\n- ", report)}", root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为 VCam 上的 CinemachinePositionComposer 写入初始默认展示参数。
|
||||
/// 这些値与 <see cref="CameraArea"/> 的默认値一致,确保脆架生成后 Scene 预览即有正确感觉。
|
||||
/// 运行时 CameraStateController.ConfigureSlot 会在每次 SwitchArea 时用 per-area 配置覆写。
|
||||
/// </summary>
|
||||
private static void ApplyComposerDefaults(CinemachinePositionComposer composer)
|
||||
{
|
||||
if (composer == null) return;
|
||||
|
||||
// 屏幕位置:玩家稍低于中心,上方有更多视野
|
||||
var comp = composer.Composition;
|
||||
comp.ScreenPosition = new Vector2(0f, -0.15f);
|
||||
comp.DeadZone.Enabled = true;
|
||||
comp.DeadZone.Size = new Vector2(0.15f, 0.05f);
|
||||
composer.Composition = comp;
|
||||
|
||||
// 阻尼:X 轻度缓冲,Y = 0(由 CameraAsymmetricDampingExtension 接管非对称 Y 阻尼)
|
||||
composer.Damping = new Vector3(0.5f, 0f, 0f);
|
||||
|
||||
// Lookahead:水平引领预测开启,IgnoreY = true(平台游戏 Y 轴不预测,避免起跳时镜头猛拉)
|
||||
var lah = composer.Lookahead;
|
||||
lah.Enabled = true;
|
||||
lah.Time = 0.28f;
|
||||
lah.Smoothing = 5f;
|
||||
lah.IgnoreY = true;
|
||||
composer.Lookahead = lah;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user