Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

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

CallEffect
FrameDriver.new(opts) -> DriverMake 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() -> booleanStart 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() -> booleanWhether the loop is running now.
driver:stats() -> Stats{ name, running, starts, steps, held, failures, lastError }.
FrameDriver.gameplayClockRunning() -> booleanFalse 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-shot execute, 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 next ensure() 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 next ensure() 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 while engine.paused is 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.timeScale reaches dt through 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.