Skip to main content

Use timers and time scaling

Add a cooldown or delay with eggy.Timer, and change how quickly gameplay time advances with app.time.setScale. Timers use counted gameplay time, so they follow time scaling and editor pause.

Choose the action to delay​

Use an existing game or plugin that imports eggy and has an update hook. The cooldown example calls fire(app), which is your game's own operation. Keep the timer in the game or component state that owns the delayed action.

1. Start a cooldown​

Store an optional timer, check it before allowing the action, and start a new timer after the action succeeds:

pub const Game = struct {
cooldown: ?eggy.Timer = null,

pub fn update(self: *Game, app: *eggy.App) !void {
if (self.cooldown) |timer| {
if (!try timer.finished(app.time)) return;
self.cooldown = null;
}
if (app.input.pressed(.space)) {
try fire(app);
self.cooldown = try app.time.startTimer(0.5);
}
}
};

finished stays true once the deadline is reached. Clear the optional or start another timer when you consume it, as this example does.

2. Inspect or cancel a delay​

For a delay that does not block the rest of update, query the value where you need it. These are the operations available on a timer:

var delay = try app.time.startTimer(0.5);
if (try delay.finished(app.time)) doSomething();
const seconds_left = try delay.remainingSeconds(app.time);
try app.time.cancelTimer(&delay);

Retain delay in state between frames if it must outlive the current hook call. doSomething() is your own action.

timer.status(app.time) returns .running, .finished, or .cancelled. remainingSeconds returns a nonnegative number, or zero for a finished/cancelled timer. A zero duration is immediately finished. Negative, NaN, and infinite durations return error.InvalidDuration.

Cancel a stored timer​

if (self.cooldown) |*timer| {
try app.time.cancelTimer(timer);
}

Cancellation mutates the value you pass, so it takes a pointer. Repeated cancellation is safe. A cancelled timer does not report finished. There is no cleanup or destructor to call. Timers allocate nothing and retain no callbacks. Cancelling one copy leaves other copies unchanged.

3. Scale movement and timers​

Use app.time.delta_s for movement that should follow the gameplay clock:

const step_m = speed_m_per_s * app.time.delta_s;
transform.translate(.{ step_m, 0, 0 });

Here transform is the object's transform and speed_m_per_s is its speed. Change the scale when gameplay needs to slow down, freeze, or resume:

try app.time.setScale(0.5); // half-speed counted time
try app.time.setScale(0); // freeze counted time
try app.time.setScale(1); // normal speed

The multiplier applies on subsequent host ticks. It does not change an already-counted frame or stop hooks and systems. Negative, NaN, and infinite multipliers are rejected, leaving the previous scale intact.

Unscaled frame steps are capped at 0.1 seconds before multiplication. A timer follows this same counted clock, including the clamp, scale, and editor pause. Use a different clock for real-world deadlines. UI tools should use their host-supplied UI clock for animation.

4. Restart timers after resets​

World resets invalidate old timer values. Querying or cancelling one against the current clock returns error.StaleTimer. Keep timers in the plugin's current state so they can be started again after reset.

For saved games and scenes, store a duration or remaining time and start a fresh timer after loading. Do not persist a clock generation or absolute gameplay deadline. Clock lifetimes explains how Play and world reload affect timers.

Check the cooldown and scale​

Run the game and press Space to fire. Pressing it again before 0.5 counted gameplay seconds have passed should not call fire again. After that interval, a new press should fire.

With scale 0.5, movement driven by delta_s and the cooldown should advance at half speed. At scale 0, those timers and movements should stop advancing, although hooks still run. Restore scale 1 to resume. To check cancellation, cancel a stored timer and confirm that finished remains false.

Clock behavior and support​

Timers and scaling do not introduce or modify a fixed-step or physics scheduler.