Cheat sheet
Frequently used Ark APIs for quick lookup.
World creation
The world is the central ECS data storage. Most applications will use exactly one world.
🌍 Create a World with default initial capacity
world := ecs.NewWorld()
_ = &worldAPI: World.New
🌍 Create a World with a specific initial capacity
world := ecs.NewWorld(1024)
_ = &worldAPI: World.New
Create entities
Entities represent the “objects” in a game or simulation.
✨ Create an entity without components
✨ A component mapper is required for creating entities with components
✨ Create a single entity, given some components
e := mapper.NewEntity(
&Position{X: 100, Y: 100},
&Velocity{X: 1, Y: -1},
)
_ = eAPI: Map2.NewEntity
✨ Create a single entity using a callback
e := mapper.NewEntityFn(func(pos *Position, vel *Velocity) {
pos.X, pos.Y = 100, 100
vel.X, vel.Y = 1, -1
})
_ = eAPI: Map2.NewEntityFn
✨ Create many entities more efficiently, all with the same component values
mapper.NewBatch(
100,
&Position{X: 100, Y: 100},
&Velocity{X: 1, Y: -1},
)API: Map2.NewBatch
✨ Create many entities, using a callback for individual initialization
mapper.NewBatchFn(
100,
func(e ecs.Entity, pos *Position, vel *Velocity) {
pos.X, pos.Y = rand.Float64()*100, rand.Float64()*100
vel.X, vel.Y = rand.NormFloat64(), rand.NormFloat64()
})API: Map2.NewBatchFn
Remove entities
❌ Remove a single entity
world.RemoveEntity(entity)API: World.RemoveEntity
❌ Remove all entities that match a filter
filter := ecs.NewFilter2[Position, Velocity](&world).Exclusive()
world.RemoveEntities(filter.Batch(), nil)API: World.RemoveEntities
❌ With a callback to do something with entities before their removal
world.RemoveEntities(filter.Batch(), func(entity ecs.Entity) {
// Do something before removal
})API: World.RemoveEntities
Add/remove components
Components store the data that is associated to entities.
🧩 A component mapper is required for adding and removing components
🧩 Add and remove components to/from a single entity
entity := world.NewEntity()
mapper.Add(
entity,
&Position{X: 100, Y: 100},
&Velocity{X: 1, Y: -1},
)
mapper.Remove(entity)API: Map2.Add, Map2.Remove
🧩 Add components to all entities matching a filter
filter := ecs.NewFilter1[Altitude](&world).Exclusive()
mapper.AddBatch(
filter.Batch(),
&Position{X: 100, Y: 100},
&Velocity{X: 1, Y: -1},
)API: Map2.AddBatch
🧩 Add components to all entities matching a filter, with individual initialization
filter := ecs.NewFilter1[Altitude](&world).Exclusive()
mapper.AddBatchFn(
filter.Batch(),
func(e ecs.Entity, pos *Position, vel *Velocity) {
pos.X, pos.Y = rand.Float64()*100, rand.Float64()*100
vel.X, vel.Y = rand.NormFloat64(), rand.NormFloat64()
})API: Map2.AddBatchFn
🧩 Remove components from all entities matching a filter.
The callback can be used to do something with entities before component removal.
filter := ecs.NewFilter2[Position, Velocity](&world)
mapper.RemoveBatch(
filter.Batch(),
func(entity ecs.Entity) { /* ... */ })API: Map2.RemoveBatch
Filters and queries
Queries are the main work horse for implementing logic.
🔍 Use filters and queries to iterate entities
Filters should be stored and re-used for best performance.
Always create a new query before iterating.
filter := ecs.NewFilter2[Position, Velocity](&world)
query := filter.Query()
for query.Next() {
pos, vel := query.Get()
pos.X += vel.X
pos.Y += vel.Y
}API: Filter1, Filter2, …, Filter2.Query, Query2.Next, Query2.Get
🔍 Filters can match additional components
For components the entities should have, but that are not accessed in the query.
filter := ecs.NewFilter2[Position, Velocity](&world).
With(ecs.C[Altitude]())
_ = filterAPI: Filter2.With
🔍 Filters can exclude components
filter := ecs.NewFilter2[Position, Velocity](&world).
Without(ecs.C[Altitude]())
_ = filterAPI: Filter2.Without
🔍 Filters can be exclusive on the given components
This filter matches only entities with exactly the given components.
filter := ecs.NewFilter2[Position, Velocity](&world).
Exclusive()
_ = filterAPI: Filter2.Exclusive
🔍 Filters can combine multiple conditions
filter := ecs.NewFilter1[Position](&world).
With(ecs.C[Velocity]()).
With(ecs.C[Altitude]()).
Without(ecs.C[Health]())
_ = filterAPI: Filter2.With, Filter2.Without
🔍 Access entities in query loops
query := filter.Query()
for query.Next() {
entity := query.Entity()
_ = entity
}API: Query.Entity
🔍 Queries can count entities without iterating
Note that a query that is not iterated must be closed explicitly.
query := filter.Query()
fmt.Println(query.Count())
query.Close()API: Query.Count, Query.Close
Access components
Components can also be accessed for arbitrary entities, not only inside queries.
🧩 A component mapper is required for component access outside queries
🧩 Access components by entity
entity := mapper.NewEntity(&Position{X: 100, Y: 100}, &Velocity{X: 1, Y: -1})
pos, vel := mapper.Get(entity)
_, _ = pos, velAPI: Map2.Get
🧩 Check for existence of components for an entity
entity := mapper.NewEntity(&Position{X: 100, Y: 100}, &Velocity{X: 1, Y: -1})
hasPosAndVel := mapper.HasAll(entity)
_ = hasPosAndVelAPI: Map2.HasAll
🧩 For single component access, there is a slightly more convenient mapper
mapper := ecs.NewMap[Position](&world)
entity := mapper.NewEntity(&Position{X: 100, Y: 100})
if mapper.Has(entity) {
pos := mapper.Get(entity)
_ = pos
}API: Map
Resources
Resources are “global”, singleton-like data structures that are not associated to a particular entity.
📦 Adding and getting resources, the simple but slower way (≈20ns)
grid := NewGrid(100, 100)
ecs.AddResource(&world, &grid)
_ = ecs.GetResource[Grid](&world)API: AddResource, GetResource
📦 For repeated access, better use a resource accessor (Get() ≈1ns)
gridResource := ecs.NewResource[Grid](&world)
grid := gridResource.Get()
_ = grid(Creating the accessor does not add the actual Grid resource!)
API: Resource, Resource.Get
Events and observers
Observers allow to react to ECS lifecycle events, like entity creation or component addition.
👀 Create and register observers for ECS lifecycle events
Gets notified on any creation of an entity.
ecs.Observe(ecs.OnCreateEntity).
Do(func(e ecs.Entity) {
// Do something with the newly created entity.
}).
Register(&world)API: Observer
👀 Observers can filter for certain components
Gets notified when a Position and a Velocity component are added to an entity.
ecs.Observe(ecs.OnAddComponents).
For(ecs.C[Position]()).
For(ecs.C[Velocity]()).
Do(func(e ecs.Entity) {
// Do something with the entity.
}).
Register(&world)API: Observer.For
👀 Observers can process matched components
Gets notified when a Position and a Velocity component are added to an entity,
with both available in the callback.
ecs.Observe2[Position, Velocity](ecs.OnAddComponents).
Do(func(e ecs.Entity, pos *Position, vel *Velocity) {
// Do something with the entity and the components
}).
Register(&world)👀 Observers can also filter for the entity composition
Gets notified when a Position component is added to an entity
that also has Velocity but not Altitude.
ecs.Observe1[Position](ecs.OnAddComponents).
With(ecs.C[Velocity]()).
Without(ecs.C[Altitude]()).
Do(func(e ecs.Entity, pos *Position) {
// Do something with the entity and the component
}).
Register(&world)📣 Custom event types can be created using a registry
var registry = ecs.EventRegistry{}
var OnCollisionDetected = registry.NewEventType()
var OnInputReceived = registry.NewEventType()
_, _ = OnCollisionDetected, OnInputReceived📣 Custom events can be emitted by the user
event := world.Event(OnCollisionDetected).
For(ecs.C[Position]())
entity := mapper.NewEntity(&Position{}, &Velocity{})
event.Emit(entity)API: World.Event, Event