Recipes
This page provides common patterns and snippets for working with Glom ECS.
Basic Queries
Find all entities with specific components
Use All to iterate through every entity that matches a
component signature.
import { All, Read, Write } from "@glom/ecs"
const moveSystem = (query: All<Write<typeof Position>, Read<typeof Velocity>>) => {
for (const [pos, vel] of query) {
pos.x += vel.dx
pos.y += vel.dy
}
}Find a unique entity (Singletons)
Use Unique to access a single entity that you know
exists, such as a global game state or a manager.
import { Unique, Read } from "@glom/ecs"
const scoreSystem = ([game]: Unique<Read<typeof GameState>>) => {
console.log("Current Score:", game.score)
}Reactive Queries
Perform an action once when a component is added
Use In to catch entities that just started matching a
signature.
import { In, Entity, Spawn, defineTag, defineRelation } from "@glom/ecs"
const Vfx = defineTag()
const LeveledUp = defineTag()
const EmitsFrom = defineRelation()
const onLevelUp = (added: In<Entity, typeof LeveledUp>, spawn: Spawn<typeof Vfx>) => {
for (const [entity] of added) {
// spawn a particle effect at the leveled-up entity
spawn(Vfx, EmitsFrom(entity))
}
}Clean up resources when a component is removed
Use Out to catch entities that no longer match a
signature.
import { Out, Entity, Despawn } from "@glom/ecs"
const onShieldRemoved = (removed: Out<Entity, typeof Shield>, despawn: Despawn) => {
for (const [entity] of removed) {
// ...
}
}Joins and Relationships
Combine two disjoint sets (Cartesian Product)
Use Join with two All queries to find all
combinations of two sets of entities.
import { Join, All, Entity, Read } from "@glom/ecs"
type Query = Join<
All<Entity, typeof Pos>,
All<Entity, typeof Pos>
>
const collide = (query: Query) => {
for (const [e1, p1, e2, p2] of query) {
if (e1 === e2) continue // skip self-collision
const dist = Math.hypot(p1.x - p2.x, p1.y - p2.y)
if (dist < 1.0) {
// handle collision
}
}
}Find related entities (Relational Join)
Use Join with a third argument to find entities linked
by a relationship.
import { Join, All, Entity, defineRelation } from "@glom/ecs"
const ChildOf = defineRelation()
type Query = Join<
All<typeof Position>,
All<typeof Position>,
typeof ChildOf
>
const updateChildren = (query: Query) => {
for (const [childPos, parentPos] of query) {
childPos.absoluteX = parentPos.x + childPos.relativeX
childPos.absoluteY = parentPos.y + childPos.relativeY
}
}Garbage collect linked entities
Use Out wrapping a relational Join to
automatically clean up “child” entities when their “parent” no longer
matches a condition (or is despawned).
import { Out, Join, All, Entity, Has, Despawn, defineRelation, defineTag } from "@glom/ecs"
const Attacking = defineTag()
const EmitsFrom = defineRelation()
type Query = Out<
Join<
All<Entity>,
All<Has<typeof Attacking>>,
typeof EmitsFrom
>
>
const cleanupBeams = (removed: Query, despawn: Despawn) => {
// triggers if the player stops attacking, or if either entity is despawned
for (const [beam] of removed) {
despawn(beam)
}
}World API
Add a component to an existing entity
Inside a system, use the Add descriptor.
import { Add, Entity, defineTag } from "@glom/ecs"
const MyTag = defineTag()
const mySystem = (query: All<Entity>, add: Add<typeof MyTag>) => {
for (const [entity] of query) {
add(entity)
}
}Spawn an entity with initial components
Use the Spawn descriptor. You can optionally use
Spawn<typeof Component> to inform the scheduler about
which components this system will be adding to the world.
import { Spawn, defineComponent } from "@glom/ecs"
const Pos = defineComponent<{x: number, y: number}>()
const spawnSystem = (spawn: Spawn<typeof Pos>) => {
const entity = spawn(Pos({x: 0, y: 0}))
}