ECS

Entities

How entities are identified, built and destroyed with the high-level Entity builder and the low-level EntityComponentDataset, and how EntityReference gives you handles that survive ID reuse.

An entity is an integer ID plus a generation counter — nothing more. The EntityComponentDataset recycles IDs when entities are removed; the generation counter makes a recycled ID distinguishable from a stale reference to the old one, without any extra bookkeeping. The data an entity carries lives in components; this page is about creating, reading, and destroying the entities themselves.

Building an entity — the high-level API

Entity is a chainable builder that accumulates components and then inserts everything into a dataset in one call:

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

const transform = new Transform();
transform.position.set(10, 0, 20);

const entityId = new Entity()
    .add(transform)
    .build(ecd);           // returns the numeric entity ID

add() returns this for chaining. If the entity is already built when you call add(), the component is forwarded directly to the dataset. entity.destroy() removes the entity from the dataset and clears the builder so it can be rebuilt later.

Entity also exposes:

MethodDescription
hasComponent(klass)Returns true if a component of that type is present
getComponent(klass)Returns the instance, or null if absent
getComponentSafe(klass)Same, but throws if absent
removeComponent(klass)Removes and returns the instance (also removes from dataset if built)
isBuilttrue after build(), false after destroy()

Reading an existing entity into a builder

If you have a numeric entity ID and a dataset, Entity.readFromDataset(id, ecd) reconstructs an Entity builder from the live components, so you can inspect or modify them with the same API.

Creating and destroying entities directly

EntityComponentDataset is the actual storage. The Entity builder calls into it; you can also use it directly when you need fine-grained control over the entity lifecycle:

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

const ecd = new EntityComponentDataset();

const id = ecd.createEntity();   // allocates an ID (reusing freed slots)
// ... attach components (see Components) ...
ecd.removeEntity(id);            // removes all components and frees the ID

Entity-level dataset methods:

MethodDescription
createEntity()Allocates a new ID (reuses freed slots)
entityExists(id)Tests whether an ID is live
getEntityGeneration(id)Returns the generation counter for a live entity
removeEntity(id)Removes all components, fires lifecycle events, frees the ID
getEntityCount()Current live entity count

Attaching and reading components on a dataset is covered in Components.

EntityReference — generation-safe handles

EntityReference stores an entity ID plus the generation counter it had when bound. Because IDs are recycled, a raw integer ID is unsafe to hold onto: after the original entity is removed, the same ID may belong to a different entity. An EntityReference whose generation no longer matches the dataset’s is treated as invalid, so you can always tell whether you still hold the entity you think you do.

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

const ref = EntityReference.bind(ecd, entityId);

if (ref.verify(ecd)) {
    const t = ecd.getComponent(ref.id, Transform);
}

ref.destroy(ecd);   // removes the entity if still valid; no-op otherwise
MemberDescription
idEntity ID (-1 when unset)
generationGeneration counter at bind time
bind(ecd, entity)Sets id and reads the current generation
verify(ecd)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)

Every Entity builder holds an EntityReference in its reference property — set on build(), cleared on destroy(). For persisting references across save and load, see Saving & loading.

Where to go next

  • Components — defining component types and attaching them.
  • Systems & scheduling — how systems read and write components each frame.
  • QueriestraverseEntities, traverseComponents, and cached iteration.