Simple Harmonic Motion
An Alternative To Lerp
What is a lerp?
The easy, boring bit
Linear Interpolate (Lerp) is a simple mathematical function that is used to find a value somewhere between two inputs. Specifically, we’re looking at the use of Lerp in relation to a transform position.
There are 3 parameters in a Lerp. The first two, a & b, could also be called Start & End, Low & High or Min & Max, depending on context.
The final parameter, t, is sometimes called Time, Alpha, Delta, Blend, or Mix.
Most math libraries support a & b being floating point numbers or vectors (and therefore also colours). In all cases, t is a floating point number.
Some functions clamp t into the range of 0 -> 1, others allow any value for t.
a + (b – a) * t;
It’s that simple. Whilst it’s good to understand the maths behind the function, bare in mind most engines have a math library which has the function built in.
How do we use it?
The classic method
There are many uses for Lerp. The one which we will be focusing on in this article is;
Each frame, interpolate a vector position from point a to point b by a fraction of delta time.
Specifically we’re looking at the following case:
lerp(currentPosition, targetPosition, deltaTime * strength)
By moving some fraction of the remaining distance between where we are currently, and where we would like to be, we move in progressively smaller steps towards our target, giving the appearance of smooth motion.
As we can see above, the movement eases in. Over the course of the movement, the velocity is largest on the first frame and smallest on last frame. This is because the distance between a and b is largest on the first frame, and lerp moves a fraction of the distance between a and b per frame.
We don’t have any concept of persistent velocity here, so the moment the target moves we get a sharp change in velocity followed by a smooth ease in.
Simple Harmonic Motion
The cool method.
An alternative way to move from point a to point b which takes current velocity into account, is called Simple Harmonic Motion. Since velocity it taken into account, an object in motion will not suddenly change path when a new target appears, it will alter course smoothly before attempting to reach a new equilibrium.
The Simple Harmonic Class class uses two parameters: Angular Frequency, and Damping Ratio. To oversimplify:
The angular frequency parameter affects how fast the current state will move towards the equilibrium state (A bit like the t value in a lerp).
The damping ratio parameter affects how ‘springy’ the motion will be.
We can under-damp (DR < 1) to allow the movement to overshoot and then attempt to recover. Under-damped harmonic motion is a really great way to add juice to UI, character movement, and physics simulations.
We can critically damp (DR == 1) to perfectly move towards the point as fast as the AF allows, without overshooting. A critically dampened spring is very similar to a lerp, but current velocity is taken into account leading to a smoother move.
We can over-damp (DR > 1), which ensures we never oscillate, but we might not reach equilibrium as quickly as possible. We haven’t found much use for over-damped springs in our game, but there are probably some cool ones out there!
For a more in depth explanation from someone who knows far more than us, check out Ryan Juckett
3D Position Interpolation
How many dimensions?
The maths being spring motion in a single dimension can easily be transferred across to multidimensional equations by simply isolating each dimension as its own spring, calculating all the springs, then recombining into a multidimensional vector.
The code included on this page has methods for 1D, 2D, and 3D calculations. If anyone was feeling especially clever it wouldn’t be difficult to add methods for 4D/Colour interpolation.
The original code comes from Ryan Juckett. We have translated his code into C# and made it Unity friendly. There are two stages to its use – The first is to call CalcDampedSpringMotionParams in order to convert from AF/DR into a set of coefficients which can be used in the Calculate method. This can be done every frame if you’re tweaking values, but should really be cached at the start of your application to avoid the expensive function call in the hot path.
We’ve opted to use ref parameters for current state and velocity.