157 lines
7.3 KiB
C#
157 lines
7.3 KiB
C#
using UnityEngine;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
|
||
namespace SLSUtilities.General
|
||
{
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
} |