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 CurveCache = new Dictionary(); /// /// 获取指定的缓动曲线 /// 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; } } } }