Search code examples
c#unity-game-enginetimecurveevaluate

Unity Curve Evaluate is normalised time, or what units?


The Evaluate function on an animation curve gets a value at a time.

https://docs.unity3d.com/ScriptReference/AnimationCurve.Evaluate.html

But it's not clear (to me) what time base or units are used.

For ParticleSystem.MinMaxCurve, this is explicitly described as being evaluated as a normalised 0 to 1 range of values of the curve's duration:

https://docs.unity3d.com/ScriptReference/ParticleSystem.MinMaxCurve.Evaluate.html

Normalized time (in the range 0 - 1, where 1 represents 100%) at which to evaluate the curve. This is valid when ParticleSystem.MinMaxCurve.mode is set to ParticleSystemCurveMode.Curve or ParticleSystemCurveMode.TwoCurves.

UPDATE:

For those considering performance: AnimationCurve's Evaluate is very fast in the traditional sense of Unity's MonoBehaviour world, having an inbuilt means of caching the lookup for the surrounding keys, and retention of the last position evaluated, too. Especially fast in loops of evaluation, because of this. Highly old school optimised, by Unity.

However, ParticleSystem.MinMaxCurve's evaluate benefits from and is part of the Jobs friendly updates to the UnityEngine.ParticleSystemJobs features.

In small usages (around 1000 evaluation steps or less), they're about the same. But with Jobs and lots of fine grained evaluation (more than 10,000), the MinMaxCurve gets ahead.


Solution

  • It uses 0 to 1 in the most cases but depends completely how you configure and use yours ... you can easily extend an AnimationCurve with keyframes before 0 and beyond 1.

    You can however get the duration of your AnimationCurve so basically you can normalize down any animation curve to a "time" value between 0 and 1 using

    public static class AniamtionCurveUtils
    {
        public static float EvaluateNormalizedTime(this AnimationCurve curve, float normalizedTime)
        {
            if(curve.length <= 0)
            {
                Debug.LogError("Given curve has 0 keyframes!");
                return float.NaN;
            }
    
             // get the time of the first keyframe in the curve
            var start = curve[0].time;
    
            if(curve.length == 1) 
            {
                Debug.LogWarning("Given curve has only 1 single keyframe!");
                return start;
            }
    
            // get the time of the last keyframe in the curve
            var end = curve[curve.length - 1].time;
           
            // get the duration fo the curve
            var duration = end - start;
            
            // get the de-normalized time mapping the input 0 to 1 onto the actual time range 
            // between start and end
            var actualTime = start + Mathf.Clamp(normalizedTime, 0, 1) * duration;
    
            // finally use that calculated time to actually evaluate the curve
            return curve.Evaluate(actualTime);
        }
    }
    

    and then e.g.

    var someValue = someCurve.EvaluateNormalizedTime(someNormalizedTime);