using UnityEngine; using Unity.Cinemachine; namespace BaseGames.Camera { /// /// Y 轴非对称阻尼扩展。实现下落快、起跳缓的非对称相机追随手感: /// - 下落时快速跟随(低阻尼):玩家落下时相机迅速移动,提前呈现地面地形; /// - 起跳时缓慢上移(高阻尼):相机不会在跳跃峰值前立刻拉高,保留地面视野。 /// /// 使用须知: /// /// 挂载在 VCamA / VCamB 上( 初始化时自动识别)。 /// 此扩展存在时, 会自动将 /// 的 Y 分量清零,避免双重阻尼。 /// 阻尼值可由 通过 DampingDown / DampingUp /// 属性 per-area 覆写,无需手动修改此组件。 /// /// [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; /// 运行时按区域写入阻尼值。 public float DampingDown { get => _dampingDown; set => _dampingDown = value; } /// 运行时按区域写入阻尼值。 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 是 CinemachinePositionComposer(Damping.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; } /// /// 重置内部平滑状态。在相机硬切(instantCut)时由 CameraStateController 调用, /// 确保新房间的 Y 坐标从目标位置开始,不受旧房间阻尼状态影响。 /// public void ResetState() => _initialized = false; } }