Schedule¶
A lot of game logic is about time. Something should happen in two seconds, or on every beat, or run for half a second and then stop. You can track each of those with a counter you tick down by hand, but that is a lot of bookkeeping once there are a few of them. The schedule module keeps those counters for you. It is a small opt-in module, imported on its own with import nim2d/schedule, and the core engine does not pull it in.
You make a Scheduler, add timers to it, and advance it once per frame from your own update with the frame's dt. When a timer comes due it calls the callback you gave it.
import nim2d
import nim2d/schedule
let sched = newScheduler() # (1)!
n2d.update = proc(nim2d: Nim2d, dt: float) =
sched.update(dt) # (2)!
- Make a scheduler to hold your timers.
- Advance it once per frame with the frame's dt; timers that come due fire here.
Once, on a repeat, or for a while¶
after runs a callback once, a set number of seconds from now.
every runs one on a repeat. Left alone it repeats forever, or you give it a count to stop after that many fires.
sched.every(1.5, proc() = spawnEnemy()) # forever
sched.every(0.2, proc() = blink(), count = 6) # six blinks, then done
during calls a callback every frame for a stretch of time and hands it the frame's dt, which is what you want for something that has to keep running for a moment rather than fire at one instant, like a screen shake or a fade. Pass a second callback to run once when it finishes.
Cancelling¶
Each of after, every and during hands back a TimerId. Keep it if you might want to stop the timer early, and pass it to cancel.
let id = sched.every(1.0, proc() = tick())
# ...
sched.cancel(id) # stop ticking
sched.clear() # or drop every timer at once
Cancelling a timer that has already fired or is not there does nothing, so you never have to guard the call.
Scheduling from a callback¶
The callbacks are plain closures, so they read whatever they need from around them, and they can schedule or cancel more timers, even themselves. A callback that re-arms itself with a fresh delay each time gives you a repeat whose gap can change from one round to the next.
A timer added from inside a callback waits until the next update to start counting, so a callback that schedules another never runs the new one in the same frame.
The schedule example wires several of these together. A metronome on every pulses a ring on each beat, each beat queues an offbeat with after, every fourth beat runs a during sweep that fills a bar, and a feed lists the callbacks as they fire so you can watch the timing line up.
See also
The runnable schedule example, and the schedule API reference.