106 lines
4.8 KiB
C#
106 lines
4.8 KiB
C#
using UnityEngine;
|
||
using Unity.Cinemachine;
|
||
|
||
namespace BaseGames.Camera
|
||
{
|
||
/// <summary>
|
||
/// 速度自适应 Lookahead 扩展。
|
||
///
|
||
/// 玩家水平速度越快,Lookahead.Time 越接近 CameraArea 配置的最大值;
|
||
/// 静止时衰减至最大值的 <see cref="_restScale"/> 倍,避免静止时镜头无谓偏移。
|
||
///
|
||
/// 挂载位置:Persistent 场景中的 VCamA / VCamB GameObject。
|
||
/// <see cref="CameraStateController.ConfigureSlot"/> 在每次切换区域时调用
|
||
/// <see cref="SetConfiguredMax"/> 传入该区域的 LookaheadTime。
|
||
/// </summary>
|
||
[AddComponentMenu("Cinemachine/Extensions/Camera Adaptive Lookahead")]
|
||
[DisallowMultipleComponent]
|
||
public class CameraAdaptiveLookaheadExtension : CinemachineExtension
|
||
{
|
||
[Tooltip("静止时 Lookahead 缩减比例(0~1)。\n" +
|
||
"0 = 静止时完全无 Lookahead;0.25 = 静止时使用配置值的 25%。\n" +
|
||
"推荐 0.2~0.3。")]
|
||
[Range(0f, 1f)]
|
||
[SerializeField] private float _restScale = 0.25f;
|
||
|
||
[Tooltip("达到最大 Lookahead 所需的水平速度(世界单位/秒)。\n" +
|
||
"玩家以此速度奔跑时 Lookahead.Time = 100% 配置值。推荐 10~15。")]
|
||
[SerializeField] private float _speedAtFullLookahead = 12f;
|
||
|
||
[Tooltip("水平速度估算的平滑强度。越大响应越快。推荐 4~6。")]
|
||
[SerializeField] private float _speedSmoothing = 5f;
|
||
|
||
// ── 内部状态 ──────────────────────────────────────────────────────────
|
||
|
||
private float _configuredMaxTime = -1f; // -1 = ConfigureSlot 尚未调用
|
||
private float _estimatedSpeedX;
|
||
private float _lastFollowX;
|
||
private bool _trackingInitialized;
|
||
|
||
// ── 公开 API ──────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 由 <see cref="CameraStateController.ConfigureSlot"/> 调用,
|
||
/// 传入当前 CameraArea 配置的最大 Lookahead 时长。
|
||
/// </summary>
|
||
public void SetConfiguredMax(float maxTime) => _configuredMaxTime = maxTime;
|
||
|
||
// ── Extension ─────────────────────────────────────────────────────────
|
||
|
||
protected override void PostPipelineStageCallback(
|
||
CinemachineVirtualCameraBase vcam,
|
||
CinemachineCore.Stage stage,
|
||
ref CameraState state,
|
||
float deltaTime)
|
||
{
|
||
if (stage != CinemachineCore.Stage.Body) return;
|
||
|
||
// 编辑器预览时不运行
|
||
if (deltaTime <= 0f)
|
||
{
|
||
_trackingInitialized = false;
|
||
return;
|
||
}
|
||
|
||
// ConfigureSlot 尚未调用时跳过(避免覆盖默认值)
|
||
if (_configuredMaxTime < 0f) return;
|
||
|
||
// ── 估算玩家水平速度 ──────────────────────────────────────────────
|
||
Transform follow = vcam.Follow;
|
||
if (follow != null)
|
||
{
|
||
if (!_trackingInitialized)
|
||
{
|
||
_lastFollowX = follow.position.x;
|
||
_trackingInitialized = true;
|
||
}
|
||
|
||
float rawSpeedX = Mathf.Abs(follow.position.x - _lastFollowX) / deltaTime;
|
||
_lastFollowX = follow.position.x;
|
||
_estimatedSpeedX = Mathf.Lerp(_estimatedSpeedX, rawSpeedX, 1f - Mathf.Exp(-deltaTime * _speedSmoothing));
|
||
}
|
||
|
||
// ── 速度映射 → Lookahead 时长 ─────────────────────────────────────
|
||
float fraction = Mathf.Clamp01(_estimatedSpeedX / _speedAtFullLookahead);
|
||
float scaledTime = Mathf.Lerp(_configuredMaxTime * _restScale, _configuredMaxTime, fraction);
|
||
|
||
var composer = vcam.GetComponent<CinemachinePositionComposer>();
|
||
if (composer == null) return;
|
||
|
||
var lah = composer.Lookahead;
|
||
lah.Time = scaledTime;
|
||
composer.Lookahead = lah;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置速度估算状态。在相机硬切时由 CameraStateController 调用,
|
||
/// 避免上一区域的奔跑速度影响新区域的初始 Lookahead 量。
|
||
/// </summary>
|
||
public void ResetState()
|
||
{
|
||
_estimatedSpeedX = 0f;
|
||
_trackingInitialized = false;
|
||
}
|
||
}
|
||
}
|