154 lines
4.7 KiB
C#
154 lines
4.7 KiB
C#
|
|
using System.Collections.Generic;
|
|||
|
|
using SLSUtilities.Feedback;
|
|||
|
|
using Unity.Cinemachine;
|
|||
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
namespace Cielonos.MainGame.Effects.Feedback
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 单个位移震动实例的运行时状态。
|
|||
|
|
/// </summary>
|
|||
|
|
public class CameraPositionShakeInstance : ShakeInstanceBase
|
|||
|
|
{
|
|||
|
|
public FloatCurveChannel intensityCurve;
|
|||
|
|
public Vector3 amplitude;
|
|||
|
|
|
|||
|
|
public CameraPositionShakeInstance(
|
|||
|
|
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 CameraPositionShakeEvent
|
|||
|
|
{
|
|||
|
|
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 上,
|
|||
|
|
/// 监听 CameraPositionShakeEvent,驱动 CinemachineCameraOffset 实现叠加震动。
|
|||
|
|
/// </summary>
|
|||
|
|
[AddComponentMenu("Cielonos/Feedback Shakers/Camera Position Shaker")]
|
|||
|
|
[RequireComponent(typeof(CinemachineCamera))]
|
|||
|
|
[RequireComponent(typeof(CinemachineCameraOffset))]
|
|||
|
|
public class CinemachinePositionShaker : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
private CinemachineCameraOffset _offsetComponent;
|
|||
|
|
private Vector3 _initialOffset;
|
|||
|
|
private readonly List<CameraPositionShakeInstance> _activeShakes = new List<CameraPositionShakeInstance>();
|
|||
|
|
|
|||
|
|
private void Awake()
|
|||
|
|
{
|
|||
|
|
_offsetComponent = GetComponent<CinemachineCameraOffset>();
|
|||
|
|
_initialOffset = _offsetComponent.Offset;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnEnable()
|
|||
|
|
{
|
|||
|
|
CameraPositionShakeEvent.Register(OnShakeEvent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnDisable()
|
|||
|
|
{
|
|||
|
|
CameraPositionShakeEvent.Unregister(OnShakeEvent);
|
|||
|
|
StopAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Update()
|
|||
|
|
{
|
|||
|
|
if (_offsetComponent == null) return;
|
|||
|
|
|
|||
|
|
if (_activeShakes.Count == 0)
|
|||
|
|
{
|
|||
|
|
_offsetComponent.Offset = _initialOffset;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Vector3 totalOffset = Vector3.zero;
|
|||
|
|
|
|||
|
|
for (int i = _activeShakes.Count - 1; i >= 0; i--)
|
|||
|
|
{
|
|||
|
|
CameraPositionShakeInstance shake = _activeShakes[i];
|
|||
|
|
shake.Tick();
|
|||
|
|
totalOffset += shake.Evaluate();
|
|||
|
|
|
|||
|
|
if (shake.IsFinished)
|
|||
|
|
{
|
|||
|
|
_activeShakes.RemoveAt(i);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_offsetComponent.Offset = _initialOffset + totalOffset;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnShakeEvent(
|
|||
|
|
FeedbackContext feedbackContext,
|
|||
|
|
FloatCurveChannel intensityCurve,
|
|||
|
|
Vector3 amplitude,
|
|||
|
|
bool stop)
|
|||
|
|
{
|
|||
|
|
if (stop) { StopAll(); return; }
|
|||
|
|
|
|||
|
|
var instance = new CameraPositionShakeInstance(
|
|||
|
|
feedbackContext.timeSettings,
|
|||
|
|
feedbackContext.player.TimeProvider,
|
|||
|
|
intensityCurve,
|
|||
|
|
amplitude,
|
|||
|
|
feedbackContext.duration
|
|||
|
|
);
|
|||
|
|
_activeShakes.Add(instance);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void StopAll()
|
|||
|
|
{
|
|||
|
|
_activeShakes.Clear();
|
|||
|
|
if (_offsetComponent != null)
|
|||
|
|
{
|
|||
|
|
_offsetComponent.Offset = _initialOffset;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|