Files
Cielonos/Assets/OtherPlugins/Feel/MMFeedbacks/MMFeedbacksForThirdParty/Cinemachine/Shakers/MMCinemachineRotationShaker.cs
2025-12-22 18:36:29 -05:00

240 lines
8.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 MoreMountains.Feedbacks;
using MoreMountains.Tools;
using System.Collections.Generic;
#if MM_CINEMACHINE
using Cinemachine;
#elif MM_CINEMACHINE3
using Unity.Cinemachine;
#endif
namespace MoreMountains.FeedbacksForThirdParty
{
// 1. 定义震动实例类 (保存单个震动的状态)
public class MMCinemachineRotationShakeInstance
{
public float Duration;
public float Timer;
public Vector3 Amplitude;
public AnimationCurve Curve;
public float Intensity;
public TimescaleModes TimescaleMode;
public MMCinemachineRotationShakeInstance(float duration, Vector3 amplitude, AnimationCurve curve, float intensity, TimescaleModes timescaleMode)
{
Duration = duration;
Timer = 0f;
Amplitude = amplitude;
Curve = curve;
Intensity = intensity;
TimescaleMode = timescaleMode;
}
}
// 2. 定义事件
public struct MMCinemachineRotationShakeEvent
{
static private event Delegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void RuntimeInitialization() { OnEvent = null; }
public static void Register(Delegate callback) { OnEvent += callback; }
public static void Unregister(Delegate callback) { OnEvent -= callback; }
public delegate void Delegate(MMF_Feedback source, AnimationCurve shakeCurve, float duration, Vector3 rotationAmplitude, float remapMin, float remapMax, bool relative,
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 void Trigger(MMF_Feedback source, AnimationCurve shakeCurve, float duration, Vector3 rotationAmplitude, float remapMin, float remapMax, bool relative,
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(source, shakeCurve, duration, rotationAmplitude, remapMin, remapMax, relative, feedbacksIntensity, channelData, resetShakerValuesAfterShake, resetTargetValuesAfterShake, forwardDirection, timescaleMode, stop, restore);
}
}
/// <summary>
/// Cinemachine Rotation Shaker (Additive / Mixing Support)
/// X/Y applied to FollowTarget, Z applied to Camera Dutch.
/// 支持多个 Feedback 同时叠加震动。
/// </summary>
[AddComponentMenu("More Mountains/Feedbacks/Shakers/Cinemachine/MM Cinemachine Rotation Shaker (Additive)")]
#if MM_CINEMACHINE
[RequireComponent(typeof(CinemachineVirtualCamera))]
#elif MM_CINEMACHINE3
[RequireComponent(typeof(CinemachineCamera))]
#endif
public class MMCinemachineRotationShaker : MMShaker
{
public Transform FollowTarget => _targetCamera.Follow;
[MMInspectorGroup("Debug Info", true, 43)]
[MMReadOnly]
public int ActiveShakesCount = 0;
#if MM_CINEMACHINE
protected CinemachineVirtualCamera _targetCamera;
#elif MM_CINEMACHINE3
protected CinemachineCamera _targetCamera;
#endif
protected float _initialDutch;
protected Quaternion _initialTargetRotation;
// 活跃震动列表
protected List<MMCinemachineRotationShakeInstance> _activeShakes = new List<MMCinemachineRotationShakeInstance>();
protected override void Initialization()
{
// 不调用 base.Initialization() 以完全接管 Update 逻辑
#if MM_CINEMACHINE
_targetCamera = GetComponent<CinemachineVirtualCamera>();
#elif MM_CINEMACHINE3
_targetCamera = GetComponent<CinemachineCamera>();
#endif
GrabInitialValues();
}
protected override void GrabInitialValues()
{
if (_targetCamera != null)
{
#if MM_CINEMACHINE
_initialDutch = _targetCamera.m_Lens.Dutch;
#elif MM_CINEMACHINE3
_initialDutch = _targetCamera.Lens.Dutch;
#endif
}
if (FollowTarget != null)
{
_initialTargetRotation = FollowTarget.localRotation;
}
}
// 自定义 Update 处理叠加逻辑
protected override void Update()
{
if (_activeShakes.Count == 0)
{
Stop();
return;
}
float totalX = 0f;
float totalY = 0f;
float totalZ = 0f;
// 倒序遍历列表
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;
// 计算当前强度
float percent = Mathf.Clamp01(shake.Timer / shake.Duration);
float curveValue = shake.Curve.Evaluate(percent);
float currentIntensity = curveValue * shake.Intensity;
// 累加 Euler 角度
totalX += shake.Amplitude.x * currentIntensity;
totalY += shake.Amplitude.y * currentIntensity;
totalZ += shake.Amplitude.z * currentIntensity;
// 移除过期震动
if (shake.Timer >= shake.Duration)
{
_activeShakes.RemoveAt(i);
}
}
// 应用总偏移
ApplyRotation(totalX, totalY, totalZ);
// 更新 Debug 计数
ActiveShakesCount = _activeShakes.Count;
// 如果所有震动结束,强制归位
if (_activeShakes.Count == 0)
{
ApplyRotation(0f, 0f, 0f);
}
}
protected virtual void ApplyRotation(float xOffset, float yOffset, float zOffset)
{
// Z -> Dutch (直接加法)
if (_targetCamera != null)
{
#if MM_CINEMACHINE
_targetCamera.m_Lens.Dutch = _initialDutch + zOffset;
#elif MM_CINEMACHINE3
_targetCamera.Lens.Dutch = _initialDutch + zOffset;
#endif
}
// X/Y -> FollowTarget (Quaternion 乘法)
if (FollowTarget != null)
{
// 注意:这里我们将叠加后的 Euler 角度转换为 Quaternion然后应用到初始旋转上
FollowTarget.localRotation = _initialTargetRotation * Quaternion.Euler(xOffset, yOffset, 0f);
}
}
public virtual void OnMMCinemachineRotationShakeEvent(MMF_Feedback source,
AnimationCurve shakeCurve, float duration, Vector3 rotationAmplitude,
float remapMin, float remapMax, bool relative, 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;
}
// 创建新实例并加入列表
MMCinemachineRotationShakeInstance newShake = new MMCinemachineRotationShakeInstance(
duration,
rotationAmplitude, // 这里的 Amplitude 已经是外部传入的基准值Intensity 在 Update 中计算
shakeCurve,
feedbacksIntensity,
timescaleMode
);
_activeShakes.Add(newShake);
Play(source);
}
public override void Stop()
{
_activeShakes.Clear();
ApplyRotation(0f, 0f, 0f);
}
protected override void ResetTargetValues()
{
base.ResetTargetValues();
Stop();
}
public override void StartListening()
{
base.StartListening();
MMCinemachineRotationShakeEvent.Register(OnMMCinemachineRotationShakeEvent);
}
public override void StopListening()
{
base.StopListening();
MMCinemachineRotationShakeEvent.Unregister(OnMMCinemachineRotationShakeEvent);
}
}
}