World
The World is a sparse-set ECS keyed by integer entity ids. Components are stored per-key; queries walk the smallest store first.
import { world, component, pos_c, type Id } from "@f0rbit/forge";
const vel_c = component<{ dx: number; dy: number }>("vel");
const w = world();
const e: Id = w.spawn( [pos_c, { x: 0, y: 0 }], [vel_c, { dx: 1, dy: 0 }],);
w.has(e, vel_c); // → trueconst r = w.get(e, pos_c); // → Result<{ x, y }, EngineError>if (r.ok) r.value; // → { x: 0, y: 0 }
w.set(e, pos_c, { x: 5, y: 0 });w.remove(e, vel_c);w.despawn(e);w.count(); // → 0| Method | Signature | Notes |
|---|---|---|
spawn | (...c: [Component<T>, T][]) => Id | Returns a fresh integer id. |
spawn_many | `(count, factory) | (specs[]) => readonly Id[]` |
despawn | (id) => Result<void, EngineError> | Removes from every store. |
despawn_marked | (...markers) => number | Bulk-despawn every entity with ALL given markers. Returns count. |
has | (id, c) => boolean | Pure check, no allocation. |
get | (id, c) => Result<T, EngineError> | kind: "component_missing" if absent. |
set | (id, c, data) => Result<void, EngineError> | Adds the component if not present and bumps the version. Errors entity_not_found if despawned. |
remove | (id, c) => Result<void, EngineError> | Errors component_missing if not present. |
query | (cs, opts?) => Query<C> | Marker components are auto-elided from the tuple. See Queries. |
count | () => number | Live entity count. |
clear | () => void | Despawns every entity. Resources are NOT cleared. |
spawn_at is exposed via world[internal].spawn_at — used by snapshot restore to preserve ids across round-trips. Not part of the public surface.
Bulk spawn — spawn_many
Section titled “Bulk spawn — spawn_many”Two forms. Use the count + factory form for grid-style fills, the array form when you already have an array of specs (one entity per element):
// (count, factory) — best when entities share a common shape parameterised by indexconst grid_ids = w.spawn_many(100, (i) => [ [pos_c, { x: (i % 20) * 16, y: Math.floor(i / 20) * 16 }], [floor_c, true],]);
// (specs) — best when you already have an array of inputsconst floor_ids = w.spawn_many( [...floor_keys].map(k => { const cell = g.unkey(k); const wp = g.cell_to_world(cell.x, cell.y); return [[pos_c, { x: wp.x, y: wp.y }], [floor_c, true]] as const; }),);Signature:
world.spawn_many: { (count: number, factory: (i: number) => readonly [Component<any>, any][]): readonly Id[]; (specs: readonly (readonly [Component<any>, any][])[]): readonly Id[];};Both forms return ids in spawn order. The runtime branches on Array.isArray(arg0). count must be a non-negative integer; the factory is called count times with i = 0..count-1.
Bulk despawn — despawn_marked
Section titled “Bulk despawn — despawn_marked”For level transitions, restarts, or “kill every X”, despawn_marked despawns every entity that has all the listed marker components:
w.despawn_marked(floor_c); // every entity with floor_cw.despawn_marked(enemy_c, alive_c); // every alive enemyw.despawn_marked(); // no-op, returns 0Signature:
world.despawn_marked(...markers: readonly Component<any>[]): number;Returns the number of entities despawned. Snapshots the matching id set before mutating, so it’s safe to call mid-iteration of unrelated queries.
For a hard reset (clear everything regardless of markers), use world.clear() instead.