// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using UltimateXR.Core; using UltimateXR.Core.Settings; using UltimateXR.Extensions.System; using UltimateXR.UI.UnityInputModule.Utils; using UnityEngine; namespace UltimateXR.Animation.Interpolation { /// /// Provides functionality to interpolate between values using a wide range of interpolation modes. /// This class also provides functionality to interpolate between 2 strings using a typewriter effect. /// public static class UxrInterpolator { #region Public Methods /// /// Smooths a float value using the previous value, new value and a smooth value between [0.0, 1.0]. /// /// Old value /// New value /// Smooth value [0.0, 1.0] where 0.0 is no smoothing and 1.0 is maximum smoothing /// Smoothed value public static float SmoothDamp(float oldValue, float newValue, float smooth) { return Mathf.Lerp(oldValue, newValue, GetSmoothInterpolationValue(smooth, Time.deltaTime)); } /// /// Smooths a position value using the last position, new position and a smooth value between [0.0, 1.0]. /// /// Old position /// New position /// Smooth value [0.0, 1.0] where 0.0 is no smoothing and 1.0 is maximum smoothing /// Smoothed position value public static Vector3 SmoothDampPosition(Vector3 oldPos, Vector3 newPos, float smooth) { return Vector3.Lerp(oldPos, newPos, GetSmoothInterpolationValue(smooth, Time.deltaTime)); } /// /// Smooths a rotation value using the last rotation, new rotation and a smooth value between [0.0, 1.0]. /// This tries to do something similar to but for rotations. /// /// Old rotation /// New rotation /// Smooth value [0.0, 1.0] where 0.0 is no smoothing and 1.0 is maximum smoothing /// Smoothed rotation value public static Quaternion SmoothDampRotation(Quaternion oldRot, Quaternion newRot, float smooth) { return Quaternion.Slerp(oldRot, newRot, GetSmoothInterpolationValue(smooth, Time.deltaTime)); } /// /// Interpolates between two floating point values using a t between range [0.0, 1.0] and a given easing. /// /// Start value /// End value /// Interpolation factor /// Easing /// Interpolated value public static float Interpolate(float a, float b, float t, UxrEasing easing) { return Interpolate(a, b, 1.0f, 0.0f, Mathf.Clamp01(t), easing); } /// /// Interpolates between two points using a t between range [0.0, 1.0] and a given easing. /// /// Start value /// End value /// Interpolation factor /// Easing /// Interpolated value public static Vector3 Interpolate(Vector3 a, Vector3 b, float t, UxrEasing easing) { return Interpolate(a, b, 1.0f, 0.0f, Mathf.Clamp01(t), easing); } /// /// Spherically interpolates (SLERP) between two quaternions using a t between range [0.0, 1.0] and a given easing. /// /// Start value /// End value /// Interpolation factor /// Easing /// Interpolated value public static Quaternion Interpolate(Quaternion a, Quaternion b, float t, UxrEasing easing) { return Interpolate(a, b, 1.0f, 0.0f, Mathf.Clamp01(t), easing); } /// /// Interpolates between two floating point values. /// /// The start value /// The end value /// /// The duration of the interpolation. If there is looping (loopMode != LoopMode.None) then it will /// specify the duration of a single loop /// /// The delay duration before the interpolation starts /// /// The time value. This value will be clamped between [delay, delay + duration] or if there is looping /// (loopMode != LoopMode.None) then it will be clamped between [delay, delay + loopedDuration]. In this case /// duration will specify the duration of the loop /// /// The interpolation method to use. See @Easing /// Which looping mode to use. See @LoopMode /// /// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the /// interpolation including all the loops. A negative value will make it loop forever. /// /// /// Tells whether to use the interpolation end value during the delay, if there is a /// specified. By default it's false, which means the interpolation start value is used during the delay. /// /// Interpolated floating point value public static float Interpolate(float startValue, float endValue, float duration, float delay, float time, UxrEasing easing, UxrLoopMode loopMode = UxrLoopMode.None, float loopedDuration = -1.0f, bool delayUsingEndValue = false) { return Interpolate(new Vector4(startValue, 0, 0, 0), new Vector4(endValue, 0, 0, 0), duration, delay, time, easing, loopMode, loopedDuration, delayUsingEndValue).x; } /// /// Interpolates between two floating point values. /// /// The start value /// The end value /// The time value /// The interpolation settings to use /// Interpolated floating point value /// /// When is null. /// public static float Interpolate(float startValue, float endValue, float time, UxrInterpolationSettings settings) { settings.ThrowIfNull(nameof(settings)); return Interpolate(new Vector4(startValue, 0, 0, 0), new Vector4(endValue, 0, 0, 0), settings.DurationSeconds, settings.DelaySeconds, time, settings.Easing, settings.LoopMode, settings.LoopedDurationSeconds, settings.DelayUsingEndValue).x; } /// /// Gets the T value used for linear interpolations like Vector3.Lerp or Quaternion.Slerp using easing and loop. /// /// Value between range [0.0f, 1.0f] /// The interpolation method to use. /// Which looping mode to use. /// /// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the /// interpolation including all the loops. A negative value will make it loop forever. /// /// The t value used to linearly interpolate using the specified parameters public static float GetInterpolationFactor(float t, UxrEasing easing, UxrLoopMode loopMode = UxrLoopMode.None, float loopedDuration = -1.0f) { return Interpolate(0.0f, 1.0f, 1.0f, 0.0f, t, easing, loopMode, loopedDuration); } /// /// Interpolates between two values /// /// The start value /// The end value /// /// The duration of the interpolation. If there is looping (loopMode != LoopMode.None) then it will /// specify the duration of a single loop /// /// The delay duration before the interpolation starts /// /// The time value. This value will be clamped between [delay, delay + duration] or if there is looping /// (loopMode != LoopMode.None) then it will be clamped between [delay, delay + loopedDuration]. In this case /// duration will specify the duration of the loop /// /// The interpolation method to use. See @Easing /// Which looping mode to use. See @LoopMode /// /// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the /// interpolation including all the loops. A negative value will make it loop forever. /// /// /// Tells whether to use the interpolation end value during the delay, if there is a /// specified. By default it's false, which means the interpolation start value is used during the delay. /// /// Interpolated value public static Vector4 Interpolate(Vector4 startValue, Vector4 endValue, float duration, float delay, float time, UxrEasing easing, UxrLoopMode loopMode = UxrLoopMode.None, float loopedDuration = -1.0f, bool delayUsingEndValue = false) { if (time < delay) { return delayUsingEndValue ? endValue : startValue; } // Compute interpolation t time = Mathf.Max(delay, time); if (!(loopMode != UxrLoopMode.None && loopedDuration < 0.0f)) { Mathf.Min(time, delay + (loopMode == UxrLoopMode.None ? duration : loopedDuration)); } float t = duration == 0.0f ? 0.0f : (time - delay) / duration; if (loopMode == UxrLoopMode.Loop) { t = t - (int)t; } else if (loopMode == UxrLoopMode.PingPong) { int loopCount = (int)t; t = t - loopCount; if ((loopCount & 1) == 1) { // It's going back t = 1.0f - t; } } return InterpolateVector4(startValue, endValue, t, easing); } /// /// Interpolates between two values /// /// The start value /// The end value /// The time value /// Interpolation settings to use /// The interpolated value /// /// When is null. /// public static Vector4 Interpolate(Vector4 startValue, Vector4 endValue, float time, UxrInterpolationSettings settings) { settings.ThrowIfNull(nameof(settings)); return Interpolate(startValue, endValue, settings.DurationSeconds, settings.DelaySeconds, time, settings.Easing, settings.LoopMode, settings.LoopedDurationSeconds, settings.DelayUsingEndValue); } /// /// Interpolates between two values. The interpolation uses SLERP. /// /// The start value /// The end value /// /// The duration of the interpolation. If there is looping (loopMode != LoopMode.None) then it will /// specify the duration of a single loop /// /// The delay duration before the interpolation starts /// /// The time value. This value will be clamped between [delay, delay + duration] or if there is looping /// (loopMode != LoopMode.None) then it will be clamped between [delay, delay + loopedDuration]. In this case /// duration will specify the duration of the loop /// /// The interpolation method to use. See @Easing /// Which looping mode to use. See @LoopMode /// /// If loopMode is not LoopMode.None then loopedDuration will specify the duration of the /// interpolation including all the loops. A negative value will make it loop forever. /// /// /// Tells whether to use the interpolation end value during the delay, if there is a /// specified. By default it's false, which means the interpolation start value is used during the delay. /// /// Interpolated value public static Quaternion Interpolate(Quaternion startValue, Quaternion endValue, float duration, float delay, float time, UxrEasing easing, UxrLoopMode loopMode = UxrLoopMode.None, float loopedDuration = -1.0f, bool delayUsingEndValue = false) { float t = Interpolate(0.0f, 1.0f, duration, delay, time, easing, loopMode, loopedDuration); return Quaternion.Slerp(startValue, endValue, t); } /// /// Interpolates between two values. The interpolation uses SLERP. /// /// The start value /// The end value /// The time value /// Interpolation settings to use /// Interpolated value /// /// When is null. /// public static Quaternion Interpolate(Quaternion startValue, Quaternion endValue, float time, UxrInterpolationSettings settings) { settings.ThrowIfNull(nameof(settings)); return Interpolate(startValue, endValue, settings.DurationSeconds, settings.DelaySeconds, time, settings.Easing, settings.LoopMode, settings.LoopedDurationSeconds, settings.DelayUsingEndValue); } /// /// Interpolates text using a typewriter effect. /// /// Start text /// End text /// Interpolation t between range [0.0, 1.0] /// /// If true, uses the rich text properties of the Unity UI text component to add invisible characters during /// interpolation. /// These invisible characters will help keeping the final text layout so that there are no line wraps or line jumps /// during the interpolation. /// /// Interpolated text /// /// See to use a format string and arguments for more /// advanced interpolation and numerical string interpolation. /// public static string InterpolateText(string startText, string endText, float t, bool isForUnityTextUI) { return InterpolateText(t, isForUnityTextUI, "{0}", startText, endText); } /// /// Interpolates text using a typewriter effect /// /// Interpolation t between range [0.0, 1.0] /// /// If true, uses the rich text properties of the Unity UI text component to add invisible characters during /// interpolation. /// These invisible characters will help keeping the final text layout so that there are no line wraps or line jumps /// during the interpolation. /// /// /// The format string (what would be the first parameter of ) /// /// /// /// Start/end pairs that will be interpolated and fed into . /// These should be sequential pairs of values of the same type that represent the start value and the end value. /// For instance format could be "{0}:{1}" and args could be startArg0, endArg0, startArg1, endArg1. /// This will print 2 interpolated values (Arg0 and Arg1) whose start and end values are defined by the other 4 /// parameters. /// /// /// The interpolation can detect numerical values (int/float) and use numerical interpolation instead of raw string /// interpolation. This can be useful for effects as seen in the examples. /// /// /// /// Simple typewriter effect to write a sentence starting from empty: (t goes from 0.0 to 1.0) /// /// InterpolateText(t, true, "{0}", string.Empty, "Welcome to the Matrix!"); /// /// /// /// Using format string args to create an increasing score animation. The numerical values are interpolated instead of /// using a typewriter effect. (t goes from 0.0 to 1.0). /// /// int finalScore = 999999; /// InterpolateText(t, true, "Final score: {0:000000}", 0, finalScore); /// /// /// Interpolated string public static string InterpolateText(float t, bool isForUnityTextUI, string formatString, params object[] formatStringArgs) { #if UNITY_EDITOR if (!(formatStringArgs.Length > 0 && formatStringArgs.Length % 2 == 0)) { if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors) { Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: The text has no arguments or the number of arguments is not even"); } return string.Empty; } #endif int numArgs = formatStringArgs.Length / 2; object[] finalArgs = new object[numArgs]; for (int i = 0; i < numArgs; i++) { if (formatStringArgs[i] == null) { if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors) { Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: Argument " + i + " is null"); } return formatStringArgs[i + numArgs] != null ? formatStringArgs[i + numArgs].ToString() : string.Empty; } if (formatStringArgs[i + numArgs] == null) { if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors) { Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: Argument " + (i + numArgs) + " is null"); } return formatStringArgs[i] != null ? formatStringArgs[i].ToString() : string.Empty; } #if UNITY_EDITOR if (!(formatStringArgs[i].GetType() == formatStringArgs[i + numArgs].GetType())) { if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors) { Debug.LogError($"{UxrConstants.AnimationModule} {nameof(InterpolateText)}: Type of argument " + i + " is not the same as argument " + (i + numArgs)); } return string.Empty; } #endif if (formatStringArgs[i] is int) { finalArgs[i] = Mathf.RoundToInt(Mathf.Lerp((int)formatStringArgs[i], (int)formatStringArgs[i + numArgs], Mathf.Clamp01(t))); } else if (formatStringArgs[i] is float) { finalArgs[i] = Mathf.Lerp((float)formatStringArgs[i], (float)formatStringArgs[i + numArgs], Mathf.Clamp01(t)); } else if (formatStringArgs[i] is string) { string a = (string)formatStringArgs[i]; string b = (string)formatStringArgs[i + 1]; int startChars = a.Length; int endChars = b.Length; int numChars = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(startChars, endChars, t)), 0, b.Length); if (Mathf.Approximately(t, 1.0f)) { finalArgs[i] = b; } else if (numChars > 0) { float letterT = Mathf.Clamp01(Mathf.Repeat(t, 1.0f / endChars) * endChars); int changingIndex = Mathf.Max(0, numChars - 1); char startCar = char.IsUpper(b[changingIndex]) ? 'A' : 'a'; char endCar = char.IsUpper(b[changingIndex]) ? 'Z' : 'z'; finalArgs[i] = b.Substring(0, Mathf.Max(0, numChars - 1)) + (char)(startCar + letterT * (endCar - startCar)); if (isForUnityTextUI) { // Add the remaining characters as "invisible" to avoid word wrapping effects during interpolation. string remaining = @"" + (endChars > startChars ? b.Substring(numChars, endChars - numChars) : string.Empty) + @""; if (!UxrRightToLeftSupport.UseRightToLeft) { finalArgs[i] += remaining; } else { finalArgs[i] = remaining + finalArgs[i]; } } } else { finalArgs[i] = string.Empty; } } } return string.Format(formatString, finalArgs); } #endregion #region Internal Methods /// /// Gets a framerate-independent smoothed interpolation value. /// /// Smooth value [0.0, 1.0] with 0 meaning no smoothing and 1 maximum smoothing /// Elapsed time in seconds /// Interpolation value [0.0, 1.0] internal static float GetSmoothInterpolationValue(float smooth, float deltaTime) { return smooth > 0.0f ? (1.0f - Mathf.Clamp01(smooth)) * deltaTime * MaxSmoothSpeed : 1.0f; } #endregion #region Private Methods /// /// Evaluates a curve using interpolation. This is the core math code that does the actual interpolation. /// /// Initial value /// End value /// Interpolation t value (range [0.0f, 1.0f]) /// Interpolation type /// Interpolated value private static Vector4 InterpolateVector4(Vector4 start, Vector4 end, float t, UxrEasing easing) { Vector4 change = end - start; switch (easing) { ///////////////////////////////////////// LINEAR //////////////////////////////////////////////////// case UxrEasing.Linear: return start + change * t; ///////////////////////////////////////// SINE ////////////////////////////////////////////////////// case UxrEasing.EaseInSine: return -change * Mathf.Cos(t * (Mathf.PI / 2.0f)) + change + start; case UxrEasing.EaseOutSine: return change * Mathf.Sin(t * (Mathf.PI / 2.0f)) + start; case UxrEasing.EaseInOutSine: return -change / 2.0f * (Mathf.Cos(Mathf.PI * t) - 1.0f) + start; case UxrEasing.EaseOutInSine when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutSine); case UxrEasing.EaseOutInSine: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInSine); ///////////////////////////////////////// QUAD ////////////////////////////////////////////////////// case UxrEasing.EaseInQuad: return start + t * t * change; case UxrEasing.EaseOutQuad: return (t - 2.0f) * t * -change + start; case UxrEasing.EaseInOutQuad: { t *= 2.0f; if (t < 1) { return change / 2.0f * t * t + start; } t -= 1.0f; return -change / 2.0f * (t * (t - 2.0f) - 1.0f) + start; } case UxrEasing.EaseOutInQuad when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutQuad); case UxrEasing.EaseOutInQuad: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInQuad); ///////////////////////////////////////// CUBIC ///////////////////////////////////////////////////// case UxrEasing.EaseInCubic: return start + t * t * t * change; case UxrEasing.EaseOutCubic: t -= 1.0f; return change * (t * t * t + 1.0f) + start; case UxrEasing.EaseInOutCubic: { t *= 2.0f; if (t < 1.0f) { return change / 2.0f * t * t * t + start; } t -= 2.0f; return change / 2.0f * (t * t * t + 2.0f) + start; } case UxrEasing.EaseOutInCubic when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutCubic); case UxrEasing.EaseOutInCubic: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInCubic); ///////////////////////////////////////// QUART ///////////////////////////////////////////////////// case UxrEasing.EaseInQuart: return start + t * t * t * t * change; case UxrEasing.EaseOutQuart: t -= 1.0f; return -change * (t * t * t * t - 1.0f) + start; case UxrEasing.EaseInOutQuart: { t *= 2.0f; if (t < 1.0f) { return change / 2.0f * t * t * t * t + start; } t -= 2.0f; return -change / 2.0f * (t * t * t * t - 2.0f) + start; } case UxrEasing.EaseOutInQuart when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutQuart); case UxrEasing.EaseOutInQuart: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInQuart); ///////////////////////////////////////// QUINT ///////////////////////////////////////////////////// case UxrEasing.EaseInQuint: return start + t * t * t * t * t * change; case UxrEasing.EaseOutQuint: t -= 1.0f; return change * (t * t * t * t * t + 1.0f) + start; case UxrEasing.EaseInOutQuint: { t *= 2.0f; if (t < 1.0f) { return change / 2.0f * t * t * t * t * t + start; } t -= 2.0f; return change / 2.0f * (t * t * t * t * t + 2.0f) + start; } case UxrEasing.EaseOutInQuint when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutQuint); case UxrEasing.EaseOutInQuint: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInQuint); ///////////////////////////////////////// EXPO ////////////////////////////////////////////////////// case UxrEasing.EaseInExpo: return change * Mathf.Pow(2.0f, 10.0f * (t - 1.0f)) + start; case UxrEasing.EaseOutExpo: return change * (-Mathf.Pow(2.0f, -10.0f * t) + 1.0f) + start; case UxrEasing.EaseInOutExpo: { t *= 2.0f; if (t < 1.0f) { return change / 2.0f * Mathf.Pow(2.0f, 10.0f * (t - 1.0f)) + start - change * 0.0005f; } t -= 1.0f; return change / 2.0f * 1.0005f * (-Mathf.Pow(2.0f, -10.0f * t) + 2.0f) + start; } case UxrEasing.EaseOutInExpo when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutExpo); case UxrEasing.EaseOutInExpo: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInExpo); ///////////////////////////////////////// CIRC ////////////////////////////////////////////////////// case UxrEasing.EaseInCirc: return -change * (Mathf.Sqrt(1.0f - t * t) - 1.0f) + start; case UxrEasing.EaseOutCirc: t -= 1.0f; return change * Mathf.Sqrt(1.0f - t * t) + start; case UxrEasing.EaseInOutCirc: { t *= 2.0f; if (t < 1.0f) { return -change / 2.0f * (Mathf.Sqrt(1.0f - t * t) - 1.0f) + start; } t -= 2.0f; return change / 2.0f * (Mathf.Sqrt(1.0f - t * t) + 1.0f) + start; } case UxrEasing.EaseOutInCirc when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutCirc); case UxrEasing.EaseOutInCirc: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInCirc); ///////////////////////////////////////// BACK ////////////////////////////////////////////////////// case UxrEasing.EaseInBack: { float s = 1.70158f; return change * (t * t * ((s + 1.0f) * t - s)) + start; } case UxrEasing.EaseOutBack: { float s = 1.70158f; t = t - 1.0f; return change * (t * t * ((s + 1.0f) * t + s) + 1.0f) + start; } case UxrEasing.EaseInOutBack: { float s = 1.70158f; t *= 2.0f; if (t < 1.0f) { s *= 1.525f; return change / 2.0f * (t * t * ((s + 1.0f) * t - s)) + start; } s *= 1.525f; t -= 2.0f; return change / 2.0f * (t * t * ((s + 1.0f) * t + s) + 2.0f) + start; } case UxrEasing.EaseOutInBack when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutBack); case UxrEasing.EaseOutInBack: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInBack); ///////////////////////////////////////// ELASTIC /////////////////////////////////////////////////// case UxrEasing.EaseInElastic: { float p = 0.3f; Vector4 a = change; float s = p / 4.0f; t -= 1.0f; return -(Mathf.Pow(2.0f, 10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a) + start; } case UxrEasing.EaseOutElastic: { float p = 0.3f; Vector4 a = change; float s = p / 4.0f; return Mathf.Pow(2.0f, -10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a + change + start; } case UxrEasing.EaseInOutElastic: { t *= 2.0f; float p = 0.3f * 1.5f; Vector4 a = change; float s = p / 4.0f; if (t < 1.0f) { t -= 1.0f; return -0.5f * (Mathf.Pow(2.0f, 10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a) + start; } t -= 1.0f; return 0.5f * Mathf.Pow(2.0f, -10.0f * t) * Mathf.Sin((t - s) * (2.0f * Mathf.PI) / p) * a + change + start; } case UxrEasing.EaseOutInElastic when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutElastic); case UxrEasing.EaseOutInElastic: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInElastic); ///////////////////////////////////////// BOUNCE //////////////////////////////////////////////////// case UxrEasing.EaseInBounce: return change - InterpolateVector4(Vector4.zero, change, 1.0f - t, UxrEasing.EaseOutBounce) + start; case UxrEasing.EaseOutBounce when t < 1.0f / 2.75f: return change * (7.5625f * t * t) + start; case UxrEasing.EaseOutBounce when t < 2.0f / 2.75f: t -= 1.5f / 2.75f; return change * (7.5625f * t * t + 0.75f) + start; case UxrEasing.EaseOutBounce when t < 2.5 / 2.75: t -= 2.25f / 2.75f; return change * (7.5625f * t * t + 0.9375f) + start; case UxrEasing.EaseOutBounce: t -= 2.625f / 2.75f; return change * (7.5625f * t * t + 0.984375f) + start; case UxrEasing.EaseInOutBounce when t < 0.5f: return InterpolateVector4(Vector4.zero, change, t * 2.0f, UxrEasing.EaseInBounce) * 0.5f + start; case UxrEasing.EaseInOutBounce: return InterpolateVector4(Vector4.zero, change, (t - 0.5f) * 2.0f, UxrEasing.EaseOutBounce) * 0.5f + change * 0.5f + start; case UxrEasing.EaseOutInBounce when t < 0.5f: return InterpolateVector4(start, start + change * 0.5f, t * 2.0f, UxrEasing.EaseOutBounce); case UxrEasing.EaseOutInBounce: return InterpolateVector4(start + change * 0.5f, end, (t - 0.5f) * 2.0f, UxrEasing.EaseInBounce); default: #if UNITY_EDITOR if (UxrGlobalSettings.Instance.LogLevelAnimation >= UxrLogLevel.Errors) { Debug.LogError($"{UxrConstants.AnimationModule}: {nameof(UxrInterpolator)} Unknown easing mode"); } #endif return Vector4.zero; } } #endregion #region Private Types & Data /// /// Constant used in SmoothDamp methods that controls the speed at which the interpolation will be performed. /// private const float MaxSmoothSpeed = 20.0f; #endregion } }