If you’ve ever wanted to change a player’s health bar from green to red as they take damage, figure out the damage of an attack based on the power level, or smoothly transition from darkness to light in your level design, chances are you’ve used a linear interpolation, or “lerp”, operation. Having linear interpolation on your toolbelt and being confident in its application can be invaluable in game programming, but I’ve noticed that there can be a few pitfalls to be aware of as well, so let’s talk about what, exactly, it means to “lerp” something, and how we can apply it in our code.

The idea behind linear interpolation is fairly straightforward: it is finding an unknown value that lies some percentage of the distance between two known values (technically, it can be an arbitrarily large set of known values, but in game dev, I’ve found that it’s most often two). For the sake of clarity and consistency, let’s call the first known value “a”, the second known value “b”, and the percentage of distance “t”. In function form, we would write it as something like this: `lerp(a, b, t)`

.

Here to demonstrate this is a little visualization tool that I built. You can drag the points around to experiment with linear interpolation between two points (the green point represents the interpolated value); hovering or touching a point or colored part of the equation at the top will bold all the matching values:

See the Pen Linear Interpolation (lerp) by Derek Stobbe (@Problematic) on CodePen.

A couple things to note. First, the arguments are all either numbers, or things composed of numbers. This means, naturally, that you can lerp between floats and integers, but you can also lerp between things like colors, numeric vectors (like the `Vector2`

and `Vector3`

types in Unity), and custom data types composed of numbers, by lerping each component value individually (Unity provides wrappers to do this for the first two via the static methods `Vector3.Lerp`

and `Color.Lerp`

).

Second, notice an interesting property of interpolation: when the (blue) `t`

value is `0`

, the green interpolated point is equal to the first (red) point. When `t = 1`

, the interpolated point is equal to the second (purple) point. Based on that, you can surmise that when `t = 0.5`

, the interpolated point is halfway between the two, `t = 0.75`

is three-quarters of the distance from `a`

to `b`

, and so on.

Note that the visualization tool allows you to explore values of `t`

that are less than `0`

or greater than `1`

, but at that point, you’re no longer *interpolating* a value, you’re *extrapolating* one. Many implementations of the `lerp`

function (including Unity’s `Mathf.Lerp`

) clamp the `t`

value between `0`

and `1`

, meaning you won’t be able to reproduce the results above where `0 > t > 1`

: when clamping input, `lerp(a, b, 1.12358)`

, for example, gives the same result as `lerp(a, b, 1.0)`

.

With this in mind, let’s talk about one of the places I frequently see developers get tripped up: using lerp to interpolate between values, where the `t`

value is driven by time. Consider this pseudocode:

1 2 3 4 5 6 7 8 9 |
function charge_attack (target_value) { player.attack_power = lerp(0, target_value, elapsed_game_time); } function update_game () { if (button_held) { charge_attack(target_value); } } |

In this example, while the player holds a button, we’d like some value representing the player’s attack power to be lerped from an initial value of 0 to `target_value`

over a period of time, say one second. However, there’s a bug in this implementation. Remember how the `t`

value of the `lerp`

function is clamped to the range `[0, 1]`

? Assuming our `elapsed_game_time`

value (which would be represented by `Time.time`

in Unity, for example) is a float that represents the number of seconds the game has been running, and begins incrementing from `0`

the moment the game starts, this code will only work as expected in the first second of gameplay. After that, the “elapsed time” will be some number greater than one, and the call to `charge_attack`

will immediately set `attack_power`

to the target value, instead of ramping it up over time, letting the player make fully-charged attacks with impunity, thus destroying our perfectly-tuned game balance.

How can we address this problem? We need to remap our `t`

value to be between 0 and 1 inclusive, and the easiest way to do that is to cache the time that the user started pressing the button. In our example implementation, that might look like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function charge_attack (target_value) { progress = elapsed_game_time - button_pressed_at; player.attack_power = lerp(0, target_value, progress); } function update_game () { if (button_pressed) { button_pressed_at = elapsed_game_time; } if (button_held) { charge_attack(target_value); } } |

The only thing we do differently is to cache the time that the button was pressed, and subtract that from the currently elapsed game time when we call `charge_attack`

to update `attack_power`

during the game loop. We can see that this works for our desired duration of one second, because say that the button is pressed at 3.1 seconds of elapsed gameplay, for example. After a half second, so at 3.6 seconds on the game clock, our calculation for the `t`

value is `3.6 - 3.1`

, which is `0.5`

… precisely the value we wanted.

What if instead of changing `attack_power`

to `target_value`

over one second, we want to lerp it over a longer period of time, say 2.5 seconds? Our implementation fails once more, because one second after the button is pressed, `elapsed_game_time - button_pressed_at`

exceeds `1.0`

, and is clamped. How can we solve this?

Well, if we look at our current solution, we can see that `elapsed_game_time - button_pressed_at`

gives us a fractional number representing our current progress toward “filling” `attack_power`

by making it equal to `target_value`

. We know that any number divided by 1 is equal to the number itself, so we can represent our progress with the formula `(elapsed_game_time - button_pressed_at) / 1.0`

. Coincidentally (or not), `1.0`

happens to represent the old requirements for the duration of the charge effect, so if we swap out that constant for a variable representing the charge duration, we’re back in business:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
charge_duration = 2.5 function charge_attack (target_value) { progress = (elapsed_game_time - button_pressed_at) / charge_duration; player.attack_power = lerp(0, target_value, progress); } function update_game () { if (button_pressed) { button_pressed_at = elapsed_game_time; } if (button_held) { charge_attack(target_value); } } |

Everything works as expected, and we have the added benefit of being able to easily tune `charge_duration`

to control how long it takes the player’s attack to lerp up to full power. Game balance remains intact!

As I’ve hopefully demonstrated, the problem domain for successfully using linear interpolation in your games mostly involves figuring out how to map your `t`

progress variable to a floating point number between 0 and 1. I’ll talk about Unity-specific implementations in another post, but I hope that’s enough to get you going for now!