152 lines
4.8 KiB
C#
152 lines
4.8 KiB
C#
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;
|
||
}
|
||
}
|
||
}
|
||
}
|