Scene
The Scene is the top-level system that owns the actor tree, drives the component lifecycle, and orchestrates per-frame updates and destruction. It acts as the bridge between the engine's Entity-Component model and the underlying Three.js THREE.Scene.
Every World holds exactly one Scene. When the game connects, the scene awakens all actors. Each animation frame follows a beginFrame → fixedUpdate/update → endFrame lifecycle that snapshots the tree once and reuses it for all updates within that frame.
Creating a scene
import { SceneEngine } from "@jolly-pixel/engine";
// Default — creates its own THREE.Scene internally
const scene = new SceneEngine();
// Or wrap an existing Three.js scene
const threeScene = new THREE.Scene();
const scene = new SceneEngine(threeScene);The underlying Three.js scene is accessible at any time through getSource():
const threeScene = scene.getSource();
threeScene.background = new THREE.Color(0x222222);Actor tree
The scene exposes a root ActorTree via the tree property. When an Actor is created without a parent, it is automatically added to this tree. All tree traversal and lookup features are available from the root:
const player = scene.tree.getActor("Player");Lifecycle
The scene drives the lifecycle of all actors and components in three phases — awake, fixedUpdate, and update — called by the World:
world.connect()
└─ scene.awake() ← awakens all existing actors
Per animation frame:
world.beginFrame()
│ └─ scene.beginFrame()
│ ├─ snapshot actors ← walk tree once, cache all actors
│ └─ start components ← newly registered components
│
├─ world.fixedUpdate(dt) ← 0..N times at fixed rate (e.g. 60 Hz)
│ └─ scene.fixedUpdate(dt)
│ └─ fixedUpdate actors ← calls actor.fixedUpdate(dt) on each
│
├─ world.update(dt) ← once per frame
│ └─ scene.update(dt)
│ └─ update actors ← calls actor.update(dt) on each
│
└─ world.endFrame()
└─ scene.endFrame()
├─ destroy components ← pending component destructions
└─ destroy actors ← pending actor destructionsAwake
When awake() is called (typically once, at connection time), the scene walks the entire tree and calls awake() on every actor that has not yet been awoken.
It then emits the "awake" event.
scene.on("awake", () => {
console.log("Scene is ready");
});beginFrame
beginFrame() is called once per animation frame. It:
- Snapshots the tree — walks all actors and caches them for the current frame. This snapshot is reused by all subsequent
fixedUpdateandupdatecalls within the same frame. - Starts components — any component queued in
componentsToBeStartedwhose actor is in the snapshot receives itsstart()call. Components whose actor is not yet in the snapshot are deferred to the next frame.
fixedUpdate
fixedUpdate(deltaTime) runs 0 to N times per frame at a constant rate (driven by FixedTimeStep). It calls actor.fixedUpdate(deltaTime) on each cached actor. Use this for physics and deterministic logic.
update
update(deltaTime) runs once per frame. It calls actor.update(deltaTime) on each cached actor. Use this for rendering-related logic.
endFrame
endFrame() is called once at the end of the animation frame. It:
- Destroys components — components queued in
componentsToBeDestroyedhave theirdestroy()hook called. - Destroys actors — actors flagged with
pendingForDestructionare recursively destroyed (children first) and removed from both the tree and the Three.js scene graph.
Destroying actors
Actors can be destroyed through the scene:
scene.destroyActor(player);This recursively destroys all children first, then removes the actor from the cached snapshot, the tree, and calls actor.destroy(). For deferred destruction (cleaned up at the end of the current frame), mark the actor as pending from the actor itself:
player.markDestructionPending();See Actor — Destruction for more details.
Destroying components
Individual components can be removed without destroying their actor:
scene.destroyComponent(component);The component is flagged as pendingForDestruction and queued. If it was still waiting for its start() call, it is removed from the start queue. The actual destroy() hook runs at the end of the current frame's update.
Events
SceneEngine extends EventEmitter and emits:
| Event | When it fires |
|---|---|
awake | After all actors have been awoken during scene.awake() |
See also
- Actor — the engine's core entity
- ActorComponent — component lifecycle and API
- ActorTree — tree traversal and lookups