Builtin Library
README.md
frameDriver
The per-frame loop a library module runs on its own behalf: the driver behind
a system that advances itself without an owner calling update(dt). The
particles package steps every autoUpdate emitter through one, and the
light cookies advance through another.
local FrameDriver = require("@builtin::modules.frameDriver")
local live = {}
local driver = FrameDriver.new({
name = "ripples",
gameplayClock = true,
hasWork = function() return #live > 0 end,
step = function(dt)
for _, ripple in ipairs(live) do ripple:advance(dt) end
end,
})
local function addRipple(ripple)
table.insert(live, ripple)
driver:ensure()
end
API
| Call | Effect |
|---|---|
FrameDriver.new(opts) -> Driver | Make a driver. opts.name names it in warnings and stats(); opts.hasWork() says whether there is work this frame; opts.step(dt) advances it; opts.gameplayClock holds the step while a play session is paused; opts.maxStep caps one frame's dt (default 0.1 s). |
driver:ensure() -> boolean | Start the loop when it is not running and there is work. Returns true when this call started it. Call it every time work is added. |
driver:isRunning() -> boolean | Whether the loop is running now. |
driver:stats() -> Stats | { name, running, starts, steps, held, failures, lastError }. |
FrameDriver.gameplayClockRunning() -> boolean | False only in a play session that is paused. |
Semantics
- The loop belongs to the module. It runs as a system task
(
task.spawnSystem), so the code that added the first piece of work (a one-shotexecute, a task scope, a component hook, a scene entrypoint) can end without ending the loop. Work added from anywhere is stepped every frame for as long as it exists. - It runs while there is work. The first frame
hasWork()answers false ends the loop; the nextensure()starts a fresh one. - A loop ended from outside is replaced.
ensure()reads the loop's task status, so a loop cancelled by its handle or halted by the watchdog reads as stopped, whether it ended at its frame wait or in the middle of a step, and the nextensure()starts another. A task a step spawns reads the loop as running while that step is on the stack. - The gameplay clock. With
gameplayClock = true, a frame that arrives in play whileengine.pausedis set calls nothing, and the next frame after the pause lifts carries on from there. In edit the step keeps arriving, as the editor's preview.engine.timeScalereachesdtthrough the scheduler. - A step that raises is caught; the loop carries on with the next frame.
The first raise of each distinct message is logged as a warning, and
stats()counts every one.