Entities & Behavior
The world node is the entity
Section titled “The world node is the entity”There is no separate entity layer in vgai — no ECS, no scene-node wrapper, no
mirror tree. The rendering library’s own node is the entity: a Three.js
Object3D in a three world, a PIXI.Container in a canvas world, a DOM
element in a dom root. Anything you can do to an entity, you do with the
library’s own API on the library’s own object.
Behavior is code in the world’s own idiom
Section titled “Behavior is code in the world’s own idiom”There is deliberately no engine component framework. Each root kind carries behavior the way its own ecosystem does:
- Three (R3F) worlds — behavior is a React component calling
useFrame. Its props are its configuration (the inspector types them straight from the JSX literal), its hooks are its state, and the engine services it needs — input, physics, audio, deterministic RNG — come from the world context (packages/engine/src/world3d-react/world-context.ts). A subsystem the world did not declare is a compile error, not a runtime surprise.
function Spinner({ speed = 1 }: { speed?: number }) { const ref = useRef<Mesh>(null); useFrame((_, dt) => { if (ref.current) ref.current.rotation.y += dt * speed; }); return ( <mesh ref={ref}> <boxGeometry /> <meshStandardMaterial color="tomato" /> </mesh> );}- Canvas (Pixi TSX) worlds — the same shape through
@pixi/react: behavior is a component callinguseTick, and the host drives the tick (deltaMSis game time — a paused world receives no ticks, so Edit and Play stay distinct without any behavior-side code). - Ingest and module roots — the game’s own source owns its loop; the
host hands it
update(dt)and layer/lifecycle, nothing more. - Dom roots — React renders from game state via
useWorldState; DOM entities host no per-frame logic.
State survives hot reload
Section titled “State survives hot reload”A world’s live component state is React state, so the usual HMR semantics apply: an edited component re-renders in place and hooks keep their values where React can preserve them. There is no engine-side instance registry to keep in sync — the world’s source is the only authority.
Finding entities at runtime
Section titled “Finding entities at runtime”Gameplay code can query a loaded graph without bookkeeping its own indexes:
queryByTag(root, tag)/queryByName(root, name)(packages/engine/src/ecs/scene-query.ts) — one-shot depth-first walks readinguserData['tags']andObject3D.name.ctx.sceneIndex— the live index over the same storage: answers without traversing and dispatchesentityadded/entityremoved/tagadded/tagremovedevents when the answer changes.
See also
Section titled “See also”- The game loop & phases — when your
useFrame/useTickcode runs. - Worlds and roots — how roots and their documents fit together.
- Engine API reference — the generated context contract.