不在触发区域时的相机逻辑

This commit is contained in:
2026-05-19 14:30:26 +08:00
parent 750baeb219
commit be96b326de
2 changed files with 53 additions and 3 deletions

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using UnityEngine;
using Unity.Cinemachine;
@@ -181,6 +182,31 @@ namespace BaseGames.Camera
}
}
// ── 静态注册表 ────────────────────────────────────────────────────────
private static readonly List<CameraArea> s_All = new();
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStaticState() => s_All.Clear();
/// <summary>
/// 从所有已激活的 <see cref="CameraArea"/> 中找出距 <paramref name="worldPos"/> 最近的一个。
/// 仅考虑拥有专属 VCam 的区域;无可用区域时返回 <c>null</c>。
/// 距离以各区域 <c>transform.position</c> 为参考点。
/// </summary>
public static CameraArea FindNearest(Vector3 worldPos)
{
CameraArea best = null;
float bestSqDist = float.MaxValue;
foreach (var area in s_All)
{
if (area == null || !area.HasDedicated) continue;
float d = (area.transform.position - worldPos).sqrMagnitude;
if (d < bestSqDist) { bestSqDist = d; best = area; }
}
return best;
}
// ── Lifecycle ────────────────────────────────────────────────────────
private void Awake()
@@ -191,6 +217,17 @@ namespace BaseGames.Camera
_dedicatedCamera.Priority = 0;
}
private void OnEnable()
{
if (Application.isPlaying && !s_All.Contains(this))
s_All.Add(this);
}
private void OnDisable()
{
s_All.Remove(this);
}
// ── Gizmo ────────────────────────────────────────────────────────────
private void OnDrawGizmosSelected()

View File

@@ -145,8 +145,12 @@ namespace BaseGames.Camera
}
/// <summary>
/// 返回当前应激活的区域:<see cref="_activeZones"/> 中优先级最高的
/// (同优先级取最近进入的),若触发集合为空则回退到 <see cref="_roomBaselineArea"/>。
/// 返回当前应激活的区域:
/// <list type="bullet">
/// <item><see cref="_activeZones"/> 不为空时,返回其中优先级最高的(同优先级取最近进入的)。</item>
/// <item>触发集合为空时,返回距玩家最近的 <see cref="CameraArea"/>
/// 无法确定玩家位置时回退到 <see cref="_roomBaselineArea"/>。</item>
/// </list>
/// </summary>
private CameraArea GetEffectiveArea()
{
@@ -154,7 +158,16 @@ namespace BaseGames.Camera
int bestPriority = -1;
foreach (var (a, p) in _activeZones)
if (p >= bestPriority) { bestPriority = p; best = a; }
return best ?? _roomBaselineArea;
if (best != null) return best;
// 触发集合为空:动态寻找距玩家最近的区域
if (_currentFollowTarget != null)
{
CameraArea nearest = CameraArea.FindNearest(_currentFollowTarget.position);
if (nearest != null) return nearest;
}
return _roomBaselineArea;
}
private void ActivateArea(CameraArea area, bool instantCut = false)