2025-12-22 18:36:29 -05:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
2026-02-13 09:22:11 -05:00
|
|
|
|
namespace SLSUtilities.General
|
2025-12-22 18:36:29 -05:00
|
|
|
|
{
|
|
|
|
|
|
public enum EaseType
|
|
|
|
|
|
{
|
|
|
|
|
|
Linear,
|
|
|
|
|
|
InSine, OutSine, InOutSine,
|
|
|
|
|
|
InQuad, OutQuad, InOutQuad,
|
|
|
|
|
|
InCubic, OutCubic, InOutCubic,
|
|
|
|
|
|
InQuart, OutQuart, InOutQuart,
|
|
|
|
|
|
InQuint, OutQuint, InOutQuint,
|
|
|
|
|
|
InExpo, OutExpo, InOutExpo,
|
|
|
|
|
|
InCirc, OutCirc, InOutCirc,
|
|
|
|
|
|
InElastic, OutElastic, InOutElastic,
|
|
|
|
|
|
InBack, OutBack, InOutBack,
|
|
|
|
|
|
InBounce, OutBounce, InOutBounce,
|
|
|
|
|
|
Flash, InFlash, OutFlash, InOutFlash
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static class Ease
|
|
|
|
|
|
{
|
|
|
|
|
|
// 缓存生成的曲线,避免重复计算
|
|
|
|
|
|
private static readonly Dictionary<EaseType, AnimationCurve> CurveCache = new Dictionary<EaseType, AnimationCurve>();
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取指定的缓动曲线
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static AnimationCurve GetCurve(EaseType easeType)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CurveCache.TryGetValue(easeType, out AnimationCurve cachedCurve))
|
|
|
|
|
|
{
|
|
|
|
|
|
return cachedCurve;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AnimationCurve newCurve = CreateCurve(easeType);
|
|
|
|
|
|
CurveCache[easeType] = newCurve;
|
|
|
|
|
|
return newCurve;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static AnimationCurve CreateCurve(EaseType easeType)
|
|
|
|
|
|
{
|
|
|
|
|
|
Keyframe[] keys;
|
|
|
|
|
|
// 采样精细度:对于复杂的曲线(如 Elastic, Bounce),采样点需要多一些
|
|
|
|
|
|
int samples = IsComplex(easeType) ? 50 : 20;
|
|
|
|
|
|
keys = new Keyframe[samples + 1];
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i <= samples; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
float t = (float)i / samples;
|
|
|
|
|
|
float v = Evaluate(easeType, t);
|
|
|
|
|
|
keys[i] = new Keyframe(t, v);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AnimationCurve curve = new AnimationCurve(keys);
|
|
|
|
|
|
|
|
|
|
|
|
// 平滑曲线切线(除线性外)
|
|
|
|
|
|
if (easeType != EaseType.Linear)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < keys.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
curve.SmoothTangents(i, 0f);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return curve;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static bool IsComplex(EaseType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
return type.ToString().Contains("Elastic") ||
|
|
|
|
|
|
type.ToString().Contains("Bounce") ||
|
|
|
|
|
|
type.ToString().Contains("Flash");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 数学计算核心:标准的 Robert Penner 缓动公式
|
|
|
|
|
|
private static float Evaluate(EaseType type, float t)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case EaseType.Linear: return t;
|
|
|
|
|
|
case EaseType.InSine: return 1f - Mathf.Cos(t * Mathf.PI * 0.5f);
|
|
|
|
|
|
case EaseType.OutSine: return Mathf.Sin(t * Mathf.PI * 0.5f);
|
|
|
|
|
|
case EaseType.InOutSine: return -(Mathf.Cos(Mathf.PI * t) - 1f) * 0.5f;
|
|
|
|
|
|
case EaseType.InQuad: return t * t;
|
|
|
|
|
|
case EaseType.OutQuad: return 1f - (1f - t) * (1f - t);
|
|
|
|
|
|
case EaseType.InOutQuad: return t < 0.5f ? 2f * t * t : 1f - Mathf.Pow(-2f * t + 2f, 2f) * 0.5f;
|
|
|
|
|
|
case EaseType.InCubic: return t * t * t;
|
|
|
|
|
|
case EaseType.OutCubic: return 1f - Mathf.Pow(1f - t, 3f);
|
|
|
|
|
|
case EaseType.InOutCubic: return t < 0.5f ? 4f * t * t * t : 1f - Mathf.Pow(-2f * t + 2f, 3f) * 0.5f;
|
|
|
|
|
|
case EaseType.InQuart: return t * t * t * t;
|
|
|
|
|
|
case EaseType.OutQuart: return 1f - Mathf.Pow(1f - t, 4f);
|
|
|
|
|
|
case EaseType.InOutQuart: return t < 0.5f ? 8f * t * t * t * t : 1f - Mathf.Pow(-2f * t + 2f, 4f) * 0.5f;
|
|
|
|
|
|
case EaseType.InQuint: return t * t * t * t * t;
|
|
|
|
|
|
case EaseType.OutQuint: return 1f - Mathf.Pow(1f - t, 5f);
|
|
|
|
|
|
case EaseType.InOutQuint: return t < 0.5f ? 16f * t * t * t * t * t : 1f - Mathf.Pow(-2f * t + 2f, 5f) * 0.5f;
|
|
|
|
|
|
case EaseType.InExpo: return t == 0f ? 0f : Mathf.Pow(2f, 10f * t - 10f);
|
|
|
|
|
|
case EaseType.OutExpo: return t == 1f ? 1f : 1f - Mathf.Pow(2f, -10f * t);
|
|
|
|
|
|
case EaseType.InOutExpo:
|
|
|
|
|
|
return t == 0f ? 0f : t == 1f ? 1f : t < 0.5f ? Mathf.Pow(2f, 20f * t - 10f) * 0.5f : (2f - Mathf.Pow(2f, -20f * t + 10f)) * 0.5f;
|
|
|
|
|
|
case EaseType.InCirc: return 1f - Mathf.Sqrt(1f - Mathf.Pow(t, 2f));
|
|
|
|
|
|
case EaseType.OutCirc: return Mathf.Sqrt(1f - Mathf.Pow(t - 1f, 2f));
|
|
|
|
|
|
case EaseType.InOutCirc:
|
|
|
|
|
|
return t < 0.5f
|
|
|
|
|
|
? (1f - Mathf.Sqrt(1f - Mathf.Pow(2f * t, 2f))) * 0.5f
|
|
|
|
|
|
: (Mathf.Sqrt(1f - Mathf.Pow(-2f * t + 2f, 2f)) + 1f) * 0.5f;
|
|
|
|
|
|
case EaseType.InBack:
|
|
|
|
|
|
{
|
|
|
|
|
|
const float s = 1.70158f;
|
|
|
|
|
|
return (s + 1f) * t * t * t - s * t * t;
|
|
|
|
|
|
}
|
|
|
|
|
|
case EaseType.OutBack:
|
|
|
|
|
|
{
|
|
|
|
|
|
const float s = 1.70158f;
|
|
|
|
|
|
return 1f + (s + 1f) * Mathf.Pow(t - 1f, 3f) + s * Mathf.Pow(t - 1f, 2f);
|
|
|
|
|
|
}
|
|
|
|
|
|
case EaseType.InOutBack:
|
|
|
|
|
|
{
|
|
|
|
|
|
const float s = 1.70158f * 1.525f;
|
|
|
|
|
|
return t < 0.5f
|
|
|
|
|
|
? (Mathf.Pow(2f * t, 2f) * ((s + 1f) * 2f * t - s)) * 0.5f
|
|
|
|
|
|
: (Mathf.Pow(2f * t - 2f, 2f) * ((s + 1f) * (t * 2f - 2f) + s) + 2f) * 0.5f;
|
|
|
|
|
|
}
|
|
|
|
|
|
case EaseType.InElastic:
|
|
|
|
|
|
return t == 0f ? 0f : t == 1f ? 1f : -Mathf.Pow(2f, 10f * t - 10f) * Mathf.Sin((t * 10f - 10.75f) * ((2f * Mathf.PI) / 3f));
|
|
|
|
|
|
case EaseType.OutElastic:
|
|
|
|
|
|
return t == 0f ? 0f : t == 1f ? 1f : Mathf.Pow(2f, -10f * t) * Mathf.Sin((t * 10f - 0.75f) * ((2f * Mathf.PI) / 3f)) + 1f;
|
|
|
|
|
|
case EaseType.InOutElastic:
|
|
|
|
|
|
const float c5 = (2f * Mathf.PI) / 4.5f;
|
|
|
|
|
|
return t == 0f ? 0f :
|
|
|
|
|
|
t == 1f ? 1f :
|
|
|
|
|
|
t < 0.5f ? -(Mathf.Pow(2f, 20f * t - 10f) * Mathf.Sin((20f * t - 11.125f) * c5)) * 0.5f :
|
|
|
|
|
|
(Mathf.Pow(2f, -20f * t + 10f) * Mathf.Sin((20f * t - 11.125f) * c5)) * 0.5f + 1f;
|
|
|
|
|
|
case EaseType.InBounce: return 1f - Evaluate(EaseType.OutBounce, 1f - t);
|
|
|
|
|
|
case EaseType.OutBounce:
|
|
|
|
|
|
if (t < 1f / 2.75f) return 7.5625f * t * t;
|
|
|
|
|
|
else if (t < 2f / 2.75f) return 7.5625f * (t -= 1.5f / 2.75f) * t + 0.75f;
|
|
|
|
|
|
else if (t < 2.5f / 2.75f) return 7.5625f * (t -= 2.25f / 2.75f) * t + 0.9375f;
|
|
|
|
|
|
else return 7.5625f * (t -= 2.625f / 2.75f) * t + 0.984375f;
|
|
|
|
|
|
case EaseType.InOutBounce:
|
|
|
|
|
|
return t < 0.5f
|
|
|
|
|
|
? (1f - Evaluate(EaseType.OutBounce, 1f - 2f * t)) * 0.5f
|
|
|
|
|
|
: (1f + Evaluate(EaseType.OutBounce, 2f * t - 1f)) * 0.5f;
|
|
|
|
|
|
|
|
|
|
|
|
// Flash 效果通常指类似锯齿波的闪烁,这里模仿 DOTween 的 Flash 逻辑(快速往复)
|
|
|
|
|
|
case EaseType.Flash: return Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f)); // 简单的往复采样
|
|
|
|
|
|
case EaseType.InFlash: return t * Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f));
|
|
|
|
|
|
case EaseType.OutFlash: return (1f - t) * Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f));
|
|
|
|
|
|
case EaseType.InOutFlash: return Mathf.SmoothStep(0, 1, t) * Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f));
|
|
|
|
|
|
|
|
|
|
|
|
default: return t;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|