Lifecycle
Mental model: lifecycle is about when the ECS does what. Understand boot, system setup, per‑frame execution, and teardown.
World lifecycle and boot sequence
When you call World.create(container, options)
IWSDK:
- Constructs a
World
instance and registers core components/systems:Transform
,Visibility
,TransformSystem
,VisibilitySystem
. - Creates Three.js objects:
Scene
,PerspectiveCamera
,WebGLRenderer
; enables WebXR. - Wraps the Scene in an entity (
world.sceneEntity
) and initializes anactiveLevel
entity beneath it. - Sets up default lighting unless disabled (gradient environment + background).
- Creates
XRInputManager
; wiresplayer
(XROrigin) andinput
into the world. - Registers core feature systems (always‑on UI, Audio; optional Locomotion/Grabbing) with explicit priorities.
- Initializes
AssetManager
. - Starts the render loop (
renderer.setAnimationLoop
): each tick setsvisibilityState
, runsworld.update(delta, time)
, then renders. - If
options.xr.offer
is 'once' or 'always', IWSDK offers an XR session after init (and re‑offers on end if 'always'). Otherwise, callworld.launchXR()
manually when the user presses your XR button. - Preloads assets (if provided) and requests an initial level load via
world.loadLevel(url?)
.
Implications
- Systems run on every animation frame in priority order before the scene is rendered.
- Default priorities (negative numbers run earlier): Locomotion (−5), Input (−4), Grabbing (−3). You can pass
{ priority: number }
when registering a system. visibilityState
is updated each frame from the XR session (ornon-immersive
).
System lifecycle (per class)
init()
- Subscribe to query
qualify
/disqualify
events. - Set up configs (
this.config.foo.subscribe(...)
). - Wire DOM/renderer listeners; enqueue cleanups into
this.cleanupFuncs
if you create disposables.
- Subscribe to query
update(delta, time)
- Iterate your query sets. Use
for (const e of this.queries.name.entities)
. - Use
.peek()
when reading config signals inside tight loops to avoid unnecessary reactivity. - Avoid allocations and nested loops in hot paths.
- Iterate your query sets. Use
destroy()
- Dispose resources and undo listeners. IWSDK calls all
cleanupFuncs
for you.
- Dispose resources and undo listeners. IWSDK calls all
Query membership and when it changes
- Adding/removing a component on an entity (
addComponent
/removeComponent
) triggers query re‑evaluation for component presence. - Changing a component value with
setValue
triggers re‑evaluation for queries whosewhere:
predicates depend on that component. getVectorView
returns a typed slice; mutating it does NOT trigger re‑evaluation on its own. If a query depends on that field, write back viasetValue
or mirror a scalar used in predicates.- Subscribe to membership edges:
ts
this.queries.panels.subscribe('qualify', (e) => {
/* attach once */
});
this.queries.panels.subscribe('disqualify', (e) => {
/* cleanup */
});
Entity lifecycle
createEntity()
creates a bare entity (noobject3D
).createTransformEntity(object?, parentOrOptions?)
creates an entity with anobject3D
, injects aTransform
, and parents it under the level root or scene based on options.entity.destroy()
marks the entity inactive, clears its bitmask, resets query membership, and detaches itsobject3D
from the scene.
Parenting rules
- If you pass
{ parent: Entity }
, the resultingobject3D
is added under the parent’sobject3D
. { persistent: true }
forces parenting under the scene (survives level swaps).- Otherwise, entities created during a level load are parented under the active level root; persistent utilities under the scene.
Frame order recap
text
set visibilityState → world.update(delta,time) → renderer.render(scene,camera)
Within world.update
, systems run in ascending priority (more negative first). Use lower (more negative) priorities for input/physics that must run before visuals.
Frame diagram (text)
text
requestAnimationFrame / XRAnimationFrame
└─ compute delta,time
├─ world.visibilityState ← session.visibilityState
├─ world.update(delta,time)
│ ├─ systems @ priority … -5, -4, -3, 0, 1 …
│ └─ queries keep sets up to date (qualify/disqualify emitted)
└─ renderer.render(scene,camera)