A fixed timestep.
loop = Cauldron2D.Loop.new(60)
{steps, loop} = Cauldron2D.Loop.advance(loop, Cauldron2D.Loop.now())
world = Enum.reduce(1..steps//1, world, fn _, w -> World.step(w, Cauldron2D.Loop.dt(loop)) end)advance/2 returns how many whole steps have come due since it was last called. dt/1 is
constant, so a simulation driven by it runs at the same speed whatever the frame rate and
replays identically given the same inputs.
Rules a caller must respect:
- The first
advance/2call establishes the baseline and returns no steps. - At most
:max_stepsare returned from one call. Time beyond that is discarded rather than carried, so the simulation loses that time instead of owing a backlog. - Time below one step is carried in the accumulator and returned by a later call.
- A
now_msearlier than the previous one contributes nothing; steps are never negative.
Summary
Functions
How many whole steps are due at now_ms, and the loop to carry forward.
The duration of one step, in seconds — the dt a simulation should use.
A loop running at hz steps per second. hz must be positive.
Types
@type t() :: %Cauldron2D.Loop{ accumulator: float(), last: integer() | nil, max_steps: pos_integer(), step_ms: float() }
Functions
@spec advance(t(), integer()) :: {non_neg_integer(), t()}
How many whole steps are due at now_ms, and the loop to carry forward.
now_ms is a millisecond reading on a single monotonic scale, as now/0 returns. Returns
{steps, loop}; pass the returned loop to the next call.
The first call on a fresh loop records now_ms as the baseline and returns 0 steps,
whatever the interval since new/2.
The duration of one step, in seconds — the dt a simulation should use.
A loop running at hz steps per second. hz must be positive.
Options
:max_steps— the most steps oneadvance/2call may return before the remaining time is discarded. Default 5
@spec now() :: integer()
The current monotonic time in milliseconds, in the form advance/2 expects.