Maths · function

dampTracking

Exponential approach of a toward a target that is itself moving, at velocity.

Explained in Coordinates and units.

function dampTracking(a: number, b: number, velocity: number, lambda: number, dt: number): number
import { dampTracking } from '@driftengine/core';

Parameters

ParameterTypeDescription
anumber
bnumber
velocitynumber
lambdanumber
dtnumber

In depth

damp above is lerp(a, b, 1 - exp(-lambda dt)), which is the exact solution of a' = lambda (b - a) for a b that holds still across the interval — a zero-order hold. For a target that is moving it is wrong in two ways that both show up as the frame rate changes, because the error is a function of dt:

  • the settled gap becomes v dt (1-k)/k for k = 1 - exp(-lambda dt), which shrinks as the frame time grows, so a machine that drops rate quietly re-frames the shot;
  • and under uneven frames the gap oscillates rather than settling at all.

This solves the same equation for b(t) = b - v (dt - t), a target arriving at b having travelled at v. Substituting e = a - b gives e' = -lambda e - v, whose solution is e(t) = (e0 + v/lambda) exp(-lambda t) - v/lambda — so the settled gap is v/lambda at every rate and there is nothing left for jitter to move. b is the target now, at the end of the interval, which is what a caller has.

What it costs is a velocity the caller has to supply, and a caller that passes zero gets damp back exactly. A finite difference of the target over the frame is the usual source and is exact for a target that is moving smoothly; what it is not good for is a target that steps, where one frame of delta/dt is a spike and this will aim v/lambda ahead of it.

Zero or negative lambda returns a rather than dividing by it. The limit as lambda goes to zero is a — no pull, no movement — so the guard agrees with the arithmetic rather than merely avoiding an infinity.