Equivalent to Lerp or SmoothStep but with custom curves?

Suppose I want a function like Mathf.Lerp or Mathf.SmoothStep where I pass in the from, to and t, but I want to define my own curves for how it converts?

Especially if I can do it visually somehow, like if there’s something in the editor that I can draw the curve it follow.

I can do visual curves with animations, but there’s a lot of extra stuff in there, and animation has to directly modify a value, while I just want to return a value from a function and then I can do what I want with it.

Is there something that works something like this?

Define a public AnimationCurve variable and you can edit the curve in the inspector.
Then instead of… Mathf.Lerp( foo, bar, t ); …call… myCurve.Evaluate( t );
edit: if you edit the curve to lie within a 0…1 range, you can do whatever you want to it programmatically to make it match your desired min/max. Looks like there are even functions to set the keys at runtime, if you need to.

2 Likes

Thanks, I hadn’t played with AnimationCurve before. It does look very promising.

Works well but will frequently crash if curves are edited in play mode.

Take a look at iTween, which is free. It may suit your needs. There are also a couple of visual editors for iTween. One of them is called iTween Multi Path, which is also free.

If someone stumbles upon this thread in order to get smoother lerps, i mostly use Mathf.Cos() lerps because they are smoother, here is a class for that:

/// <summary>
/// Lerping curves with different characteristics. Not clamped, only inputs between 0 and 1
/// </summary>
public static class LerpCurves{
    //verify plots by inserting the commented functions into this plotter https://rechneronline.de/function-graphs/
    //Cosinus soft in soft out from 0F to 1F. Coroutine useage example:
    //    float time    = 1F;
    //    float timer    = 0F;
    //    while(timer < time){
    //        float lerpFactor =    LerpCurves.SoftestInSoftOut01(timer/time);
    //        image.color = new Color(1F,1F,1F,lerpFactor); //Do something with lerpFactor e.g. this creates a fade in
    //        yield return null;
    //        timer += Time.deltaTime;
    //    }
    //    image.color = new Color(1F,1F,1F,1F); //fully finish the lerp at the end

    public    static float SofterInSofterOut01    ( float valueFrom0to1) { return                    (-Mathf.Cos(valueFrom0to1 * Mathf.PI))*0.5F+0.5F;        }    //    ( cos(x*pi)*0.5+0.5)
    public    static float SofterInSofterOut10    ( float valueFrom0to1) { return    1F -            (-Mathf.Cos(valueFrom0to1 * Mathf.PI))*0.5F+0.5F;        }    //   1-( cos(x*pi)*0.5+0.5)
    public    static float SoftestInSoftOut01        ( float valueFrom0to1) { return        Mathf.Pow(    (-Mathf.Cos(valueFrom0to1 * Mathf.PI))*0.5F+0.5F, 2F);    }    //     (-cos(x*pi)*0.5+0.5)^2
    public    static float SoftestInSoftOut10        ( float valueFrom0to1) { return    1F -Mathf.Pow(    (-Mathf.Cos(valueFrom0to1 * Mathf.PI))*0.5F+0.5F, 2F);    }    //    1-(-cos(x*pi)*0.5+0.5)^2
 
    //pretty much the same as SofterInSofterOut
    public    static float SmoothStep01            ( float valueFrom0to1) { return        valueFrom0to1*valueFrom0to1*(3-2*valueFrom0to1);                    }    //     (x*x * (3 - 2*x))
    public    static float SmoothStep10            ( float valueFrom0to1) { return    1F- valueFrom0to1*valueFrom0to1*(3-2*valueFrom0to1);                    }    //    1-(x*x * (3 - 2*x))
 
    //Symmetric, SoftestInSoftOut is not
    public    static float SmootherStep01            ( float valueFrom0to1) { return        valueFrom0to1*valueFrom0to1*valueFrom0to1*(valueFrom0to1*(6F*valueFrom0to1-15F)+10F);    }    //     x*x*x * (x* (6*x - 15) + 10)
    public    static float SmootherStep10            ( float valueFrom0to1) { return    1F- valueFrom0to1*valueFrom0to1*valueFrom0to1*(valueFrom0to1*(6F*valueFrom0to1-15F)+10F);    }    //    1-x*x*x * (x* (6*x - 15) + 10)

    public    static float Linear01                ( float valueFrom0to1) { return        valueFrom0to1;                                                        }    //        x
 
    public    static float SoftInHardOut01        ( float valueFrom0to1) { return        Mathf.Pow(valueFrom0to1,3);                                        }    //        x^3
    public    static float SoftInHardOut10        ( float valueFrom0to1) { return        1-Mathf.Pow(valueFrom0to1,3);                                        }    //    1-   x^3
    public    static float HardInSoftOut01        ( float valueFrom0to1) { return        1-Mathf.Pow(1-valueFrom0to1,3);                                        }    //     1-(1-x)^3
    public    static float HardInSoftOut10        ( float valueFrom0to1) { return        Mathf.Pow(1-valueFrom0to1,3);                                        }    //    (1-x)^3
}
4 Likes

In case anybody else tries to use SofterInSofterOut and is wondering why only these two don’t work as expected, there’s a small typo. Just reverse these two function names and voila! :slight_smile:

BTW thanks for these, they offer some nice diversity to regular Lerp and SmoothStep!

1 Like

Thanks, i changed the original code above

this is awesome! Thanks so much.
For me, the last 4 work in reverse, it appears that they return 1 and then move to 0. The final 4 functions all do this. Is this the intended behavior?

yup, this is an error, look at the comment on the right, there is the required function, just remove the “1-” in front of the 0 to 1 functions and you should be good.

I edited my post above, but i didn’t test them, but i checked the graphs
https://www.desmos.com/calculator/hm5xbp7qi4