The world
World owns the Babylon scene, the simulation clock and everything that lives in it. A game has one.
The fixed step#
Simulation runs at a fixed rate (60 Hz by default, fixedStep), decoupled from rendering. Each rendered frame calls world.advance(realDt): it runs as many fixed steps as fit in the elapsed time, then the per-frame updates. A stall is never simulated as one long jump: frame time is capped at maxFrame (0.1 s by default).
Because the step is fixed, a run with the same inputs repeats exactly. That is what makes replays, networking and tests possible: tests drive a world with world.step() or world.run(seconds) and no renderer.
| Member | Use |
|---|---|
timeScale | global slow motion (below 1) or fast forward |
paused | stops the simulation; rendering goes on |
time, tickCount | simulated seconds and number of fixed steps so far |
alpha | how far the rendered frame is between the last two steps, for interpolating what the camera sees |
entity.timeDilation | per-entity dilation: a game may slow its NPCs without slowing the player |
Entities and components#
An Entity wraps a Babylon TransformNode and carries components. A Component is a plain class with a few optional hooks:
class Bob extends Component<{ amplitude: number }> {
onAttach() { /* attached to an entity */ }
onStart() { /* before the first update */ }
onFixedUpdate(dt) { /* simulation: at the fixed rate */ }
onUpdate(dt) { /* presentation: once per rendered frame */ }
onDetach() { /* removed */ }
}
const e = world.spawn('crate', { position: new BABYLON.Vector3(0, 1, 0), tags: ['prop'] });
e.add(Bob, { amplitude: 0.1 });
Put game logic in onFixedUpdate and things that only need to look right in onUpdate. Components are found with entity.get(Cls) or require(Cls); the world finds entities by id, name, tag or component (entity, findByName, findByTag, entitiesWith).
params are plain data. That is deliberate: a level manifest names a component and gives its params, and Registry maps the name to the class (world.components, world.behaviours). A game registers its classes once; levels then wire them without code.
Services#
A Service is a world-wide singleton: input, levels, zones, physics, UI. It has an optional init(world), fixedUpdate(dt) (before the components' fixed update), update(dt) and dispose(). Add one with world.addService(s), find it with world.service('name'). createGame adds input and physics; you add what the game uses.
Events and commands#
EventBus is a small synchronous bus (on, once, off, emit) on world.events. The engine reports what happens through it: trigger:enter, trigger:exit, character:jump, character:land, physics:contact, world:step and many more, each documented where it is emitted.
CommandChannel (world.commands) is the other side: named commands with a description that a tool, the inspector or an AI agent can list and call. Every package registers its own; see Commands.
Logging#
Logger writes by scope and level to sinks; on OS Beroun a sink posts to the shell's log, in a browser to the console.