Tick Loop
Dreamlab simulates the game world at a fixed tick-rate (60 TPS by default).
Every tick the engine
- advances physics & transforms,
- executes your Behavior callbacks,
- sends a network snapshot, and then
- uses interpolation to render buttery-smooth frames on any monitor refresh rate.
A fixed tick-rate keeps gameplay deterministic and makes networking much simpler—most of the time you don't even need deltaTime
; the engine handles it for you.
1 · What happens each tick?
Phase | Typical work |
---|---|
onPreTick | Read inputs, clear one-frame buffers |
Physics & engine sims | Move rigidbodies, resolve collisions |
onTick | Main gameplay logic |
onPostTick | “Late update” logic (e.g., camera follow) |
Network snapshot | Sync entities in the world root |
onFrame (client) | Extra rendering per display frame (optional) |
The order onPreTick → onTick → onPostTick is guaranteed;
within each band, Behaviors execute in an undefined order.
onTick
vs onPostTick
Think of a tick as “prepare the next world state”:
onTick
- move players, run AI, update health bars.- Physics resolves all overlaps.
onPostTick
- world state is final → update camera, parallax, etc.
Behaviors do not tick in a guaranteed order. If you absolutely need to guarantee that one Behavior ticks before the other, it's recommended you refactor your code to enforce ordering using function calls; for example the following is incorrect:
class BehaviorOne extends Behavior {
onTick() {
// prepare some data that BehaviorTwo needs
this.prepareSomeData();
}
}
class BehaviorTwo extends Behavior {
onTick() {
// BAD, not guaranteed to run after BehaviorOne.onTick!
this.consumeData();
}
}
The correct way to script this would be:
class BehaviorOne extends Behavior {
onTick() {
// prepare some data that BehaviorTwo needs
prepareSomeData();
this.game.entities
.lookupByBehavior(BehaviorTwo)
.forEach(e => e.getBehavior(BehaviorTwo).consumeData());
}
}
Make dependencies explicit; don't rely on incidental ordering.
2 · Platform-specific hooks
Hook | Runs on… | Use for… |
---|---|---|
onInitializeClient | client only | Play VFX, set up UI |
onInitializeServer | server only | Spawn match controller, load map file |
onTickClient / Server | each tick | Divergent client/server logic |
TypeScript can't narrow this.game
automatically; guard with if (this.game.isClient())
when you need the specialised type.
3 · Interpolation
After each tick, Dreamlab interpolates between the previous and current transforms so motion stays fluid on 144 Hz, 240 Hz, etc.
- + Smooth visuals on any display
- - Adds ~1 tick (≈ 16 ms at 60 TPS) of visual latency
4 · Quick troubleshooting
Symptom | Likely fix |
---|---|
Twitchy / jittery objects | Ensure you update each entity's position only once per tick. |
Camera feels one-frame late | Move characters in onTick ; update camera in onPostTick . |
Network rubber-banding | Two peers share authority—call takeAuthority() on one side. |