Files
zeling_v2/Assets/_Game/Scripts/Camera/CameraAdaptiveLookaheadExtension.cs
2026-05-19 11:50:21 +08:00

106 lines
4.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = 静止时完全无 Lookahead0.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;
}
}
}