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:
| Method | Description |
|---|---|
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) |
isBuilt | true 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:
| Method | Description |
|---|---|
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
| Member | Description |
|---|---|
id | Entity ID (-1 when unset) |
generation | Generation 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.NULL | A 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.
- Queries —
traverseEntities,traverseComponents, and cached iteration.