ECS

Scenes & references

How Scene wraps an EntityComponentDataset for isolated worlds, how SceneManager handles transitions, how EntityReference survives entity ID reuse, and how SerializationMetadata marks transient entities.

Scene

A Scene is a named container that owns an EntityComponentDataset. Multiple scenes can exist at the same time; only one is active (attached to the EntityManager) at a time.

import Scene from "@woosh/meep-engine/src/engine/scene/Scene.js";

class MainScene extends Scene {
    constructor() {
        super("main");
    }

    async setup(options, engine, success, failure) {
        // build your world here — add entities to this.dataset
        success();
    }

    teardown(engine, success, failure) {
        // clean up
    }
}

Key Scene members:

MemberDescription
nameUnique string identifier
datasetThe scene’s EntityComponentDataset
activeObservedBooleantrue while this scene is the active one
destroyedSet to true once the scene is torn down
speedModifiersList<LinearModifier> — clock speed multipliers active with this scene
setup(options, engine, success, failure)Override to build the world; call success() when done
teardown(engine, success, failure)Override to release resources
clear()Clears the dataset (removes all entities)

SceneManager

SceneManager owns a collection of Scene instances and keeps exactly one attached to the EntityManager at a time.

// Typical access through engine.scenes (if using EngineHarness)
const sm = engine.scenes;

sm.add(new MainScene());
sm.set("main");           // deactivates current scene, activates "main"

Key SceneManager methods:

MethodDescription
create(name)Creates a plain Scene with that name and registers it
add(scene)Registers a pre-built scene
set(name)Transitions to the named scene (synchronous dataset swap)
getByName(name)Looks up a scene by name
exists(name)Returns true if a scene with that name is registered
remove(name)Unregisters and deactivates a scene
stackPush(name)Saves the current scene name, then transitions
stackPop()Returns to the previously pushed scene
clear()Detaches the current scene and dataset

When set transitions, it calls handlePreDeactivation / handlePostDeactivation on the outgoing scene and handlePreActivation / handlePostActivation on the incoming one. The EntityManager’s dataset is swapped between the two calls, so all system observers see the new entity set before handlePostActivation runs.

For transitions that need to wait for meshes and terrain tiles to load, use transitionToScene:

import { transitionToScene } from "@woosh/meep-engine/src/engine/scene/transitionToScene.js";

await transitionToScene({ scene: myScene, engine, tasks: [myLoadTask] });

transitionToScene shows a loading screen, waits for visible terrain tiles and mesh assets, compiles materials, renders one warm-up frame, and resolves once everything is ready.

EntityReference — generation-safe handles

EntityReference stores an entity ID plus its generation counter. When an entity ID is recycled, the generation counter in the dataset increments; a stale EntityReference whose generation does not match the current one is treated as invalid.

import { EntityReference } from "@woosh/meep-engine/src/engine/ecs/EntityReference.js";

// Bind a reference to a live entity
const ref = EntityReference.bind(ecd, entityId);

// Later — check if the entity is still the same one
if (ref.verify(ecd)) {
    const t = ecd.getComponent(ref.id, Transform);
}

// Destroy the entity through the reference (no-ops if already gone)
ref.destroy(ecd);

EntityReference members:

MemberDescription
idEntity ID (-1 when unset)
generationGeneration counter at bind time
bind(ecd, entity)Sets id and reads the current generation from the dataset
verify(ecd)Returns true if the entity still exists and the generation matches
destroy(ecd)Removes the entity if valid; returns false if invalid
equals(other)Compares both id and generation
EntityReference.NULLA frozen invalid reference (id = -1, generation = -1)

The Entity builder holds an EntityReference in its reference property — it is set on build() and cleared on destroy().

SerializationMetadata — transient entities

SerializationMetadata is a component that controls how an entity is treated during serialisation. The main use is marking an entity as transient so it is excluded from save files:

import { SerializationMetadata } from "@woosh/meep-engine/src/engine/ecs/components/SerializationMetadata.js";

new Entity()
    .add(new Transform())
    .add(SerializationMetadata.Transient)   // shared frozen singleton
    .build(ecd);

SerializationMetadata.Transient is a pre-built frozen instance with SerializationFlags.Transient set. Because it is frozen and shared, you should not mutate it — create a new instance if you need a different flag combination.

Every entity in the examples that should not be saved — obstacles, the player, dynamic props, input controllers — carries SerializationMetadata.Transient. This is the standard idiom.

SerializationFlags:

FlagValueMeaning
Transient1Do not include this entity in serialised output

For per-component transience (marking one component on an otherwise persistent entity), set the COMPONENT_SERIALIZATION_TRANSIENT_FIELD symbol on the component instance:

import { COMPONENT_SERIALIZATION_TRANSIENT_FIELD } from "@woosh/meep-engine/src/engine/ecs/storage/COMPONENT_SERIALIZATION_TRANSIENT_FIELD.js";

myComponent[COMPONENT_SERIALIZATION_TRANSIENT_FIELD] = true;

Where to go next