Files
zeling_v2/Assets/_Game/Scripts/Camera/CameraAsymmetricDampingExtension.cs
2026-05-17 07:56:12 +08:00

86 lines
3.9 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>
/// Y 轴非对称阻尼扩展。实现下落快、起跳缓的非对称相机追随手感:
/// - <b>下落时快速跟随</b>(低阻尼):玩家落下时相机迅速移动,提前呈现地面地形;
/// - <b>起跳时缓慢上移</b>(高阻尼):相机不会在跳跃峰值前立刻拉高,保留地面视野。
///
/// <para>使用须知:</para>
/// <list type="bullet">
/// <item>挂载在 VCamA / VCamB 上(<see cref="CameraStateController"/> 初始化时自动识别)。</item>
/// <item>此扩展存在时,<see cref="CameraStateController.ConfigureSlot"/> 会自动将
/// <see cref="CinemachinePositionComposer.Damping"/> 的 Y 分量清零,避免双重阻尼。</item>
/// <item>阻尼值可由 <see cref="CameraArea"/> 通过 <c>DampingDown</c> / <c>DampingUp</c>
/// 属性 per-area 覆写,无需手动修改此组件。</item>
/// </list>
/// </summary>
[AddComponentMenu("Cinemachine/Extensions/Camera Asymmetric Damping")]
[DisallowMultipleComponent]
public class CameraAsymmetricDampingExtension : CinemachineExtension
{
[Tooltip("相机向下(下落)时的 Y 轴阻尼(秒)。\n" +
"越小跟随越快,玩家能提前看到地面。推荐 0.05 ~ 0.15。")]
[SerializeField] private float _dampingDown = 0.08f;
[Tooltip("相机向上(起跳)时的 Y 轴阻尼(秒)。\n" +
"越大跟随越慢,保留地面视野、不会在起跳瞬间拉高。推荐 0.5 ~ 0.8。")]
[SerializeField] private float _dampingUp = 0.65f;
/// <summary>供 <see cref="CameraStateController"/> 运行时按区域写入阻尼值。</summary>
public float DampingDown { get => _dampingDown; set => _dampingDown = value; }
/// <summary>供 <see cref="CameraStateController"/> 运行时按区域写入阻尼值。</summary>
public float DampingUp { get => _dampingUp; set => _dampingUp = value; }
private float _smoothedY;
private bool _initialized;
protected override void PostPipelineStageCallback(
CinemachineVirtualCameraBase vcam,
CinemachineCore.Stage stage,
ref CameraState state,
float deltaTime)
{
if (stage != CinemachineCore.Stage.Body) return;
// deltaTime <= 0编辑器预览 / 初始帧,重置平滑器避免脏状态
if (deltaTime <= 0f)
{
_initialized = false;
return;
}
// 此时 state.RawPosition 是 CinemachinePositionComposerDamping.y = 0输出的"理想"位置
float idealY = state.RawPosition.y;
if (!_initialized)
{
_smoothedY = idealY;
_initialized = true;
return;
}
// idealY < _smoothedY → 相机目标在当前位置下方(玩家下落) → 低阻尼快速跟随
// idealY > _smoothedY → 相机目标在当前位置上方(玩家起跳) → 高阻尼缓慢跟随
float damping = idealY < _smoothedY ? _dampingDown : _dampingUp;
float t = damping > 0f
? 1f - Mathf.Exp(-deltaTime / damping)
: 1f;
_smoothedY = Mathf.LerpUnclamped(_smoothedY, idealY, t);
var pos = state.RawPosition;
pos.y = _smoothedY;
state.RawPosition = pos;
}
/// <summary>
/// 重置内部平滑状态。在相机硬切instantCut时由 CameraStateController 调用,
/// 确保新房间的 Y 坐标从目标位置开始,不受旧房间阻尼状态影响。
/// </summary>
public void ResetState() => _initialized = false;
}
}