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; } } }