marp | paginate | math | title |
---|---|---|---|
true |
true |
mathjax |
Math 3. Interpolation |
- "Lerp", or linear interpolation, is a commonly used function in gamedev
- returns the value
, which goes from to , when goes from to - when
, - when
, ("halfway") - when
,
- returns the value
- In Unity, the most basic lerp function is
Mathf.Lerp(a, b, t)
float x1 = Mathf.Lerp(5.0f, 15.0f, 0f); // returns 5.0f
float x2 = Mathf.Lerp(5.0f, 15.0f, 0.5f); // returns 10.0f
float x3 = Mathf.Lerp(5.0f, 15.0f, 1.0f); // returns 15.0f
- what if t is smaller than 0 or larger than 1?
- Unity's
Mathf.Lerp
is clamps the returned value automatically-
is at minimum and at maximum
-
- with
Mathf.LerpUnclamped
, the value is extrapolated when outside the limits!
- Some data types are more complicated than just one float value
- You can of course lerp every number value individually
- Other way around: some types have their own built-in lerps
- Unclamped versions exist, too.
- Lerping usually happens in some sort of an update function, where we can use lerp to slowly change a value from
a
tob
, while time moves forward.[SerializeField] Transform endTransform; [SerializeField] float lerpDuration; Vector3 startPosition; float startTime; bool lerping = false; void StartLerp() { startTime = Time.time; lerping = true; } void UpdateLerp() { if (!lerping) return float time = (Time.time - startTime) / lerpDuration; transform.position = Vector3.Lerp(startPosition, endTransform.position, time); if (time > 1) lerping = false; } void Update() { if (Input.GetKey("space")) StartLerp(); UpdateLerp(); }
After pressing a button once, lerp GameObject's color from red to blue.
Bonus: After pressing twice, lerp the color back to red. Bonus bonus: What if you press the button during lerping?
- Smooth interpolation
- Mathf.SmoothStep: Like Lerp but with smoothing in start and finish
- Mathf.SmoothDamp: Spring-like motion towards destination
- Mathf.MoveTowards: Move linearly towards destination with a max speed limit
- Angle versions (These take into account that the angle loops from 360 back to 0)
- Lerping is a linear operation: the rate of change is constant during the process
- Sometimes we want smoothing that is controlled more precisely than with SmoothStep and the like
- Luckily, we can also create custom curves
-
for custom interpolation curves, use the
AnimationCurve
variable[SerializeField] AnimationCurve curve;
-
The curve can be manipulated in the inspector:
- Click on the curve images on the bottom to choose and edit them
- If the curve starts from 0 and ends in 1, you can use it as a replacement for
Mathf.Lerp
- If the curve starts from and ends in 0, you can create an animation that loops back to the initial value!
-
curve.Evaluate(t)
returns a value from the graph (by default, between 0 and 1)- So we can lerp between a and b if we just supply this to a lerp function!
-
Mathf.Lerp(a, b, t)
Mathf.Lerp(a, b, curve.Evaluate(t))
public AnimationCurve bounce;
...
if(startTime > 0) // If we have set a startTime, do the interpolation
{
// Calculate valid time for curve (between 0 and 1)
float time = (Time.time - startTime) / bounceLenght;
// Get the value from curve at the time of the animation
// and multiply it with the desired scaled axis
// then add it to default scale (1, 1, 1)
transform.localScale = Vector2.one + axis * bounce.Evaluate(time);
}
- You may have seen lerp performed "on the fly" like this:
transform.position = Vector3.Lerp(transform.position, target.position, Time.deltaTime);
- The start point changes every frame! And instead of a time parameter, there's
deltaTime
...? What!? - This isn't what lerp was meant to be used for, but this can be a useful trick
- This creates a deceleration ("braking") in the end which results in a smoother finish than lerp normally does (and it finishes faster than expected)
-
The interpolation isn't linear anymore!
-
- This is often used to make dynamic objects reach an end position gradually
- Example: Camera that follows a bit behind the player character
- This creates a deceleration ("braking") in the end which results in a smoother finish than lerp normally does (and it finishes faster than expected)
- There's one important caveat, though...
-
This is bad! We get different results with different machines!
-
There's a somewhat-known solution to this
- Instead of
source = Mathf.Lerp(source, target, smoothing * Time.deltaTime);
- Do this:
source = Mathf.Lerp(source, target, 1 - Mathf.Pow(smoothing, Time.deltaTime))
- Instead of
-
Read more here: Frame rate independent damping using lerp
- In the link, there's a techincal explanation why deltatime lerping works how it works, and why the solution above fixes the framerate dependence.
Vector3 relativePos = target.position + new Vector3(0,.5f,0) - transform.position;
transform.localRotation =
Quaternion.Slerp(
transform.localRotation,
Quaternion.Lookrotation(relativePos),
Time.deltaTime
);
transform.Translate(0,0, 3 * Time.deltaTime);
-
Script Reference: Inverse lerp
Mathf.InverseLerp(a, b, x)
- Returns the answer to the inverse problem of Lerp:
- "When we know a value
that is between and , what is ?" - In other words, how far is
between and , as a fraction
- "When we know a value
-
What if we want to map a range
to range ? -
Or in other words, map a variable
input
in rangeinputMin
inputMax
- And get a result that is in a range
outputMin
outputMax
.
- And get a result that is in a range
-
The function that achieves this is a sort of a "generalization" of lerp & inverse lerp:
float Remap (float inputMin, float inputMax, float outputMin, float outputMax, float input) { float t = Mathf.InverseLerp(inputMin, inputMax, input ); return Mathf.Lerp( outputMin, outputMax, t ); }
- If
input
has the value ofinputMin
, the function returns the valueoutputMin
.
- If
- Learn: Linear interpolation
- gamedevbeginner.com: The right way to Lerp in Unity
- easings.net: a source for different easing functions beyond linear interpolation