using UnityEngine; using MoreMountains.Feedbacks; using MoreMountains.Tools; using Unity.Cinemachine; using System.Collections.Generic; namespace MoreMountains.FeedbacksForThirdParty { // 定义一个类来保存单个震动实例的状态 public class MMCinemachinePositionShakeInstance { public float Duration; public float Timer; public Vector3 Amplitude; public AnimationCurve Curve; public float Intensity; public TimescaleModes TimescaleMode; public MMCinemachinePositionShakeInstance(float duration, Vector3 amplitude, AnimationCurve curve, float intensity, TimescaleModes timescaleMode) { Duration = duration; Timer = 0f; Amplitude = amplitude; Curve = curve; Intensity = intensity; TimescaleMode = timescaleMode; } } [AddComponentMenu("More Mountains/Feedbacks/Shakers/Cinemachine/MM Cinemachine Position Shaker (Additive)")] [RequireComponent(typeof(CinemachineCamera))] [RequireComponent(typeof(CinemachineCameraOffset))] // 确保有 Offset 组件 public class MMCinemachinePositionShaker : MMShaker { [MMInspectorGroup("Debug Info", true, 43)] [MMReadOnly] public int ActiveShakesCount = 0; // 仅用于监视当前有多少个震动在叠加 protected CinemachineCamera _cmCamera; protected CinemachineCameraOffset _positionOffset; protected Vector3 _initialOffset; // 维护一个活跃震动的列表 protected List _activeShakes = new List(); protected override void Initialization() { // 注意:我们不调用 base.Initialization(),因为我们不想使用 MMShaker 默认的 Event 监听逻辑, // 我们需要完全接管 Shake 的逻辑来实现叠加。 // 但为了兼容性,如果 Feel 内部有依赖,可以保留,但这里主要逻辑是自定义的。 _cmCamera = GetComponent(); _positionOffset = GetComponent(); GrabInitialValues(); } protected override void GrabInitialValues() { if (_positionOffset != null) { _initialOffset = _positionOffset.Offset; } } // 我们重写 Update 来处理叠加逻辑 protected override void Update() { if (_positionOffset == null) return; // 如果没有活跃的震动,且当前不是初始位置,则复位(可选,作为保险) if (_activeShakes.Count == 0) { Stop(); return; } Vector3 totalShakeOffset = Vector3.zero; // 倒序遍历以便安全移除 for (int i = _activeShakes.Count - 1; i >= 0; i--) { var shake = _activeShakes[i]; // 更新时间 float deltaTime = (shake.TimescaleMode == TimescaleModes.Scaled) ? Time.deltaTime : Time.unscaledDeltaTime; shake.Timer += deltaTime; // 计算进度 (0 到 1) float percent = Mathf.Clamp01(shake.Timer / shake.Duration); // 采样曲线并计算位移 float curveValue = shake.Curve.Evaluate(percent); Vector3 currentOffset = shake.Amplitude * curveValue * shake.Intensity; // 叠加 totalShakeOffset += currentOffset; // 如果结束,移除 if (shake.Timer >= shake.Duration) { _activeShakes.RemoveAt(i); } } // 应用叠加后的总偏移量 + 初始偏移量 _positionOffset.Offset = _initialOffset + totalShakeOffset; //Debug.Log($"Total Shake Offset Applied: {totalShakeOffset}"); // 更新 Debug 计数 ActiveShakesCount = _activeShakes.Count; // 如果所有震动都结束了,强制归位以消除浮点误差 if (_activeShakes.Count == 0) { _positionOffset.Offset = _initialOffset; } } /// /// 接收事件 /// public virtual void OnPositionShakeEvent(MMF_Feedback source, AnimationCurve shakeCurve, float duration, Vector3 positionAmplitude, float feedbacksIntensity = 1.0f, MMChannelData channelData = null, bool resetShakerValuesAfterShake = true, bool resetTargetValuesAfterShake = true, bool forwardDirection = true, TimescaleModes timescaleMode = TimescaleModes.Scaled, bool stop = false) { if (!CheckEventAllowed(channelData)) return; if (stop) { Stop(); return; } // 创建新的震动实例并添加到列表 MMCinemachinePositionShakeInstance newShake = new MMCinemachinePositionShakeInstance( duration, positionAmplitude, shakeCurve, feedbacksIntensity, timescaleMode ); _activeShakes.Add(newShake); Play(source); } // 强制停止所有震动 public override void Stop() { _activeShakes.Clear(); if (_positionOffset != null) { _positionOffset.Offset = _initialOffset; } } protected override void ResetTargetValues() { base.ResetTargetValues(); Stop(); } public override void StartListening() { // 注意:这里我们依然使用基类的注册机制,或者直接在这里注册 base.StartListening(); MMCinemachinePositionShakeEvent.Register(OnPositionShakeEvent); } public override void StopListening() { base.StopListening(); MMCinemachinePositionShakeEvent.Unregister(OnPositionShakeEvent); } } }