This formula updates value
so a constant fraction of the difference from the value
and the target
is removed each frame. Let alpha = 1/(someFactor+1)
. Then we can rewrite updating to the new value'
as:
value' = (1-alpha) * value + alpha * target
Then basic algebra says
value' - target = (1-alpha) * value + alpha * target - target
value' - target = (1-alpha) * value + (alpha-1) * target
value' - target = (1-alpha) * value - (1-alpha) * target
value' - target = (1-alpha) * (value - target)
Let u
be the difference between value and target, i.e. u = value - target
and u' = value' - target
. Thus:
u' = (1-alpha) * u
Here we have an option; I will pick the one that doesn't involve calculus. We can repeat this process:
u'' = (1-alpha) * u' = (1-alpha) (1-alpha) * u
We can see the pattern is that after n
frames, the difference from the target is (1-alpha)^n * u_initial
. Let's assume that your game runs at some framerate, and you have adjusted it to your liking. Then after x
seconds, n = x/dt_nominal
frames will have passed, where dt_nominal
is the typical dt when you adjusted it (so 16.6ms for 60fps). So now u
will be (1-alpha)^(x/dt_nominal) * u_initial
.
The beauty is that this formula is defined for any real x
, not just ones which make the exponent a natural number. So here we can replace x=dt
as the duration of a frame, and get (1-alpha)^(dt/dt_nominal) * u_initial
. But, using some properties of the exponential:
(1-alpha)^(dt/dt_nominal) = e^(-dt * ln(1/(1-alpha))/dt_nominal)
So now we can write what the difference is after two frames:
u'' = exp(-dt2 * ln(1/(1-alpha))/dt_nominial)
* exp(-dt1 * ln(1/(1-alpha))/dt_nominial) * u_initial
u'' = exp(-(dt1+dt2) * ln(1/(1-alpha))/dt_nominal)
Basically, this formula lets us calculate what the value will be at any arbitrary point in the future. We just choose our update rule to calculate the value dt
seconds in the future. We also got the nice property that our solution is independent of how many frames occur or their duration, because the final value depends only on the sum of the durations, even though we calculate it individually for each frame.
So we can make our update rule:
beta = ln(1/(1-alpha))/dt_nominial
= ln((someFactor+1)/someFactor)/dt_nominial
amount = exp(-dt * beta)
value = (amount) * value + (1-amount)* target
Note that beta
is a constant. You can figure out what beta
is for your code, and going forward adjust beta
directly rather than using someFactor
. It turns out beta
is related to the half-life: it is the 1/e-life, instead of the 1/2-life.
BONUS
If you assume your frame times are all reasonably small and so is beta
, then a useful approximation is
exp(-dt*beta) ~= 1 - dt * beta
Thus your update rule is:
beta = ln(1/(1-alpha))/dt_nominial
value = (1 - dt * beta) * value + (dt * beta) * target
In the inner loop you therefore avoid a transcendental function. In UI code, use exp
. If you are moving particles, use the approximation.