Files
Cielonos/Assets/Scripts/MainGame/Effects/Feedbacks/Shakers/Cinemachine/CinemachineRotationShaker.cs
SoulliesOfficial 0902ca8a9e Bezi回来了
2026-04-28 15:46:32 -04:00

152 lines
4.8 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 System.Collections.Generic;
using SLSUtilities.Cinemachine;
using SLSUtilities.Feedback;
using Unity.Cinemachine;
using UnityEngine;
namespace Cielonos.MainGame.Effects.Feedback
{
/// <summary>
/// 单个旋转震动实例的运行时状态。
/// </summary>
public class CameraRotationShakeInstance : ShakeInstanceBase
{
public FloatCurveChannel intensityCurve;
public Vector3 amplitude;
public CameraRotationShakeInstance(
FeedbackTimeSettings timeSettings,
IFeedbackTimeProvider timeProvider,
FloatCurveChannel intensityCurve,
Vector3 amplitude,
float duration)
: base(timeSettings, timeProvider, duration)
{
this.intensityCurve = intensityCurve;
this.amplitude = amplitude;
}
/// <summary>
/// 计算当前帧的偏移量。
/// </summary>
public Vector3 Evaluate()
{
float normalizedTime = duration > 0 ? timer / duration : 1f;
float curveValue = intensityCurve.Evaluate(normalizedTime);
return amplitude * curveValue;
}
}
/// <summary>
/// 摄像机旋转震动事件,用于解耦 Action 与 Shaker。
/// </summary>
public struct CameraRotationShakeEvent
{
private static event ShakeDelegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void RuntimeInitialization() { OnEvent = null; }
public delegate void ShakeDelegate(
FeedbackContext feedbackContext,
FloatCurveChannel intensityCurve,
Vector3 amplitude,
bool stop
);
public static void Register(ShakeDelegate callback) { OnEvent += callback; }
public static void Unregister(ShakeDelegate callback) { OnEvent -= callback; }
public static void Trigger(
FeedbackContext feedbackContext,
FloatCurveChannel intensityCurve,
Vector3 amplitude,
bool stop = false)
{
OnEvent?.Invoke(feedbackContext, intensityCurve, amplitude, stop);
}
}
/// <summary>
/// Cinemachine 旋转震动器。挂载于 CinemachineCamera 上,
/// 监听 CameraRotationShakeEvent驱动 CinemachineRotationOffset 实现叠加震动。
/// X/Y 作用于 FollowTarget 旋转Z 作用于 Dutch 倾斜。
/// </summary>
[AddComponentMenu("Cielonos/Feedback Shakers/Camera Rotation Shaker")]
[RequireComponent(typeof(CinemachineCamera))]
[RequireComponent(typeof(CinemachineRotationOffset))]
public class CinemachineRotationShaker : MonoBehaviour
{
private CinemachineRotationOffset _rotationOffset;
private Vector3 _initialRotation;
private readonly List<CameraRotationShakeInstance> _activeShakes = new List<CameraRotationShakeInstance>();
private void Awake()
{
_rotationOffset = GetComponent<CinemachineRotationOffset>();
_initialRotation = _rotationOffset.rotationOffset;
}
private void OnEnable()
{
CameraRotationShakeEvent.Register(OnShakeEvent);
}
private void OnDisable()
{
CameraRotationShakeEvent.Unregister(OnShakeEvent);
StopAll();
}
private void Update()
{
if (_rotationOffset == null) return;
if (_activeShakes.Count == 0)
{
_rotationOffset.rotationOffset = _initialRotation;
return;
}
Vector3 totalOffset = Vector3.zero;
for (int i = _activeShakes.Count - 1; i >= 0; i--)
{
CameraRotationShakeInstance shake = _activeShakes[i];
shake.Tick();
totalOffset += shake.Evaluate();
if (shake.IsFinished)
{
_activeShakes.RemoveAt(i);
}
}
_rotationOffset.rotationOffset = _initialRotation + totalOffset;
}
private void OnShakeEvent(
FeedbackContext feedbackContext,
FloatCurveChannel intensityCurve,
Vector3 amplitude,
bool stop)
{
if (stop) { StopAll(); return; }
_activeShakes.Add(new CameraRotationShakeInstance(
feedbackContext.timeSettings,
feedbackContext.player.TimeProvider,
intensityCurve,
amplitude,
feedbackContext.duration
));
}
private void StopAll()
{
_activeShakes.Clear();
if (_rotationOffset != null)
{
_rotationOffset.rotationOffset = _initialRotation;
}
}
}
}