using UnityEngine;
using MoreMountains.Feedbacks;
using MoreMountains.Tools;
// 强制引用 Cinemachine 3 的命名空间
using Unity.Cinemachine;
namespace MoreMountains.FeedbacksForThirdParty
{
///
/// 定义 Shake 事件,用于传递距离震动的参数
///
public struct MMCinemachineCameraDistanceShakeEvent
{
public delegate void Delegate(AnimationCurve distortionCurve, float duration, float remapMin, float remapMax, bool relativeDistortion = false,
float feedbacksIntensity = 1.0f, MMChannelData channelData = null, bool resetShakerValuesAfterShake = true, bool resetTargetValuesAfterShake = true, bool forwardDirection = true,
TimescaleModes timescaleMode = TimescaleModes.Scaled, bool stop = false, bool restore = false);
public static event Delegate OnEvent;
public static void Trigger(AnimationCurve distortionCurve, float duration, float remapMin, float remapMax, bool relativeDistortion = false,
float feedbacksIntensity = 1.0f, MMChannelData channelData = null, bool resetShakerValuesAfterShake = true, bool resetTargetValuesAfterShake = true, bool forwardDirection = true,
TimescaleModes timescaleMode = TimescaleModes.Scaled, bool stop = false, bool restore = false)
{
OnEvent?.Invoke(distortionCurve, duration, remapMin, remapMax, relativeDistortion, feedbacksIntensity, channelData, resetShakerValuesAfterShake, resetTargetValuesAfterShake, forwardDirection, timescaleMode, stop, restore);
}
}
///
/// 这个 Shaker 能够控制 CinemachineCamera 的 CameraDistance。
/// 它会自动寻找 CinemachineThirdPersonFollow 或 CinemachinePositionComposer 组件。
///
[AddComponentMenu("More Mountains/Feedbacks/Shakers/Cinemachine/MM Cinemachine Camera Distance Shaker")]
[RequireComponent(typeof(CinemachineCamera))]
public class MMCinemachineCameraDistanceShaker : MMShaker
{
[MMInspectorGroup("Camera Distance", true, 42)]
[Tooltip("是否在原始距离的基础上进行增减 (Relative),还是直接覆盖为新距离")]
public bool RelativeDistance = true; // 默认改为True通常更符合Distance的直觉
[Tooltip("控制距离变化的曲线")]
public AnimationCurve ShakeDistance = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.5f, 1), new Keyframe(1, 0));
[Tooltip("曲线 0 对应的值")]
public float RemapDistanceZero = 0f;
[Tooltip("曲线 1 对应的值")]
public float RemapDistanceOne = 5f;
protected CinemachineCamera _targetCamera;
protected CinemachineThirdPersonFollow _thirdPersonFollow;
protected CinemachinePositionComposer _positionComposer;
protected float _initialDistance;
protected float _originalShakeDuration;
protected bool _originalRelativeDistance;
protected AnimationCurve _originalShakeDistance;
protected float _originalRemapDistanceZero;
protected float _originalRemapDistanceOne;
protected override void Initialization()
{
base.Initialization();
_targetCamera = this.gameObject.GetComponent();
// 尝试获取 ThirdPersonFollow
_thirdPersonFollow = this.gameObject.GetComponent();
// 如果没有 Follow,尝试获取 PositionComposer
if (_thirdPersonFollow == null)
{
_positionComposer = this.gameObject.GetComponent();
}
}
protected virtual void Reset()
{
ShakeDuration = 0.5f;
}
protected override void Shake()
{
// 计算当前帧的 Distance
float newDistance = ShakeFloat(ShakeDistance, RemapDistanceZero, RemapDistanceOne, RelativeDistance, _initialDistance);
SetCameraDistance(newDistance);
}
protected virtual void SetCameraDistance(float newDistance)
{
// 为了安全,防止距离为负数(如果这不是你想要的效果)
if (newDistance < 0f) newDistance = 0f;
if (_thirdPersonFollow != null)
{
_thirdPersonFollow.CameraDistance = newDistance;
}
else if (_positionComposer != null)
{
_positionComposer.CameraDistance = newDistance;
}
}
protected override void GrabInitialValues()
{
if (_thirdPersonFollow != null)
{
_initialDistance = _thirdPersonFollow.CameraDistance;
}
else if (_positionComposer != null)
{
_initialDistance = _positionComposer.CameraDistance;
}
else
{
// 如果两个组件都没找到,记录一个警告或者默认为0
// Debug.LogWarning("MMCinemachineCameraDistanceShaker: No compatible component (ThirdPersonFollow or PositionComposer) found on " + name);
}
}
protected override void ResetTargetValues()
{
base.ResetTargetValues();
SetCameraDistance(_initialDistance);
}
protected override void ResetShakerValues()
{
base.ResetShakerValues();
ShakeDuration = _originalShakeDuration;
ShakeDistance = _originalShakeDistance;
RemapDistanceZero = _originalRemapDistanceZero;
RemapDistanceOne = _originalRemapDistanceOne;
RelativeDistance = _originalRelativeDistance;
}
public virtual void OnMMCinemachineCameraDistanceShakeEvent(AnimationCurve distortionCurve, float duration, float remapMin, float remapMax, bool relativeDistortion = false,
float feedbacksIntensity = 1.0f, MMChannelData channelData = null, bool resetShakerValuesAfterShake = true, bool resetTargetValuesAfterShake = true, bool forwardDirection = true,
TimescaleModes timescaleMode = TimescaleModes.Scaled, bool stop = false, bool restore = false)
{
if (!CheckEventAllowed(channelData)) return;
if (stop) { Stop(); return; }
if (restore) { ResetTargetValues(); return; }
if (!Interruptible && Shaking) return;
_resetShakerValuesAfterShake = resetShakerValuesAfterShake;
_resetTargetValuesAfterShake = resetTargetValuesAfterShake;
if (resetShakerValuesAfterShake)
{
_originalShakeDuration = ShakeDuration;
_originalShakeDistance = ShakeDistance;
_originalRemapDistanceZero = RemapDistanceZero;
_originalRemapDistanceOne = RemapDistanceOne;
_originalRelativeDistance = RelativeDistance;
}
if (!OnlyUseShakerValues)
{
TimescaleMode = timescaleMode;
ShakeDuration = duration;
ShakeDistance = distortionCurve;
RemapDistanceZero = remapMin * feedbacksIntensity;
RemapDistanceOne = remapMax * feedbacksIntensity;
RelativeDistance = relativeDistortion;
ForwardDirection = forwardDirection;
}
Play();
}
public override void StartListening()
{
base.StartListening();
MMCinemachineCameraDistanceShakeEvent.OnEvent += OnMMCinemachineCameraDistanceShakeEvent;
}
public override void StopListening()
{
base.StopListening();
MMCinemachineCameraDistanceShakeEvent.OnEvent -= OnMMCinemachineCameraDistanceShakeEvent;
}
}
}