154 lines
4.9 KiB
C#
154 lines
4.9 KiB
C#
using System.Collections.Generic;
|
|
using SLSUtilities.Feedback;
|
|
using Unity.Cinemachine;
|
|
using UnityEngine;
|
|
|
|
namespace Cielonos.MainGame.Effects.Feedback
|
|
{
|
|
public class CameraDistanceShakeInstance : ShakeInstanceBase
|
|
{
|
|
public FloatCurveChannel distanceCurve;
|
|
|
|
public CameraDistanceShakeInstance(FeedbackTimeSettings timeSettings, IFeedbackTimeProvider timeProvider,
|
|
FloatCurveChannel distanceCurve, float duration) : base(timeSettings, timeProvider, duration)
|
|
{
|
|
this.distanceCurve = distanceCurve;
|
|
}
|
|
}
|
|
|
|
public struct CameraDistanceShakeEvent
|
|
{
|
|
private static event ShakeDelegate OnEvent;
|
|
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
|
private static void RuntimeInitialization() { OnEvent = null; }
|
|
|
|
public delegate void ShakeDelegate(
|
|
FeedbackContext feedbackContext,
|
|
FloatCurveChannel distanceCurve,
|
|
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 distanceCurve,
|
|
bool stop = false)
|
|
{
|
|
OnEvent?.Invoke(feedbackContext, distanceCurve, stop);
|
|
}
|
|
}
|
|
|
|
[AddComponentMenu("Cielonos/Feedback Shakers/Camera Distance Shaker")]
|
|
[RequireComponent(typeof(CinemachineCamera))]
|
|
[RequireComponent(typeof(CinemachineOrbitalFollow))]
|
|
public class CameraDistanceShaker : MonoBehaviour
|
|
{
|
|
private CinemachineCamera _camera;
|
|
private CinemachineOrbitalFollow _orbitalFollow;
|
|
|
|
public float initialDistance;
|
|
|
|
[Tooltip("防止因为同类 Feedback 叠加导致距离过小")]
|
|
public float minDistance = 1f;
|
|
[Tooltip("防止因为同类 Feedback 叠加导致距离过大")]
|
|
public float maxDistance = 30f;
|
|
|
|
private readonly List<CameraDistanceShakeInstance> _activeShakes = new List<CameraDistanceShakeInstance>();
|
|
|
|
private void Awake()
|
|
{
|
|
_camera = GetComponent<CinemachineCamera>();
|
|
_orbitalFollow = GetComponent<CinemachineOrbitalFollow>();
|
|
initialDistance = _orbitalFollow.Radius;
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
CameraDistanceShakeEvent.Register(OnShakeEvent);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
CameraDistanceShakeEvent.Unregister(OnShakeEvent);
|
|
StopAll();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (_orbitalFollow == null) return;
|
|
|
|
if (_activeShakes.Count == 0)
|
|
{
|
|
SetDistance(initialDistance);
|
|
return;
|
|
}
|
|
|
|
float additiveDistance = 0f;
|
|
float absoluteDistance = 0f;
|
|
bool hasAbsolute = false;
|
|
|
|
for (int i = _activeShakes.Count - 1; i >= 0; i--)
|
|
{
|
|
CameraDistanceShakeInstance shake = _activeShakes[i];
|
|
shake.Tick();
|
|
float normalizedTime = shake.timer / shake.duration;
|
|
|
|
if (shake.distanceCurve.relativeToInitial)
|
|
{
|
|
additiveDistance += shake.distanceCurve.Evaluate(normalizedTime);
|
|
}
|
|
else
|
|
{
|
|
absoluteDistance = shake.distanceCurve.Evaluate(normalizedTime);
|
|
hasAbsolute = true;
|
|
}
|
|
|
|
if (shake.IsFinished)
|
|
{
|
|
_activeShakes.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
float finalDistance = hasAbsolute ? absoluteDistance : initialDistance + additiveDistance;
|
|
|
|
// 核心防御:钳制最终距离,防止多技能/反馈叠加导致的距离暴走
|
|
finalDistance = Mathf.Clamp(finalDistance, minDistance, maxDistance);
|
|
|
|
SetDistance(finalDistance);
|
|
}
|
|
|
|
private void OnShakeEvent(
|
|
FeedbackContext feedbackContext,
|
|
FloatCurveChannel distanceCurve,
|
|
bool stop)
|
|
{
|
|
if (stop) { StopAll(); return; }
|
|
|
|
var instance = new CameraDistanceShakeInstance(
|
|
feedbackContext.timeSettings,
|
|
feedbackContext.player.TimeProvider,
|
|
distanceCurve,
|
|
feedbackContext.duration
|
|
);
|
|
_activeShakes.Add(instance);
|
|
}
|
|
|
|
private void SetDistance(float distance)
|
|
{
|
|
_orbitalFollow.Radius = distance;
|
|
}
|
|
|
|
private void StopAll()
|
|
{
|
|
_activeShakes.Clear();
|
|
if (_orbitalFollow != null)
|
|
{
|
|
SetDistance(initialDistance);
|
|
}
|
|
}
|
|
}
|
|
}
|