Files
Cielonos/Assets/Scripts/SLSUtilities/General/Ease.cs
SoulliesOfficial 50ee502684 完善
2026-02-13 09:22:11 -05:00

157 lines
7.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}
}