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()
_ = &world
API: World.New
🌍 Create a World with a specific initial capacity
world := ecs.NewWorld(1024)
_ = &world
API: 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},
)
_ = e
API: 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
})
_ = e
API: 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]())
_ = filter
API: Filter2.With
🔍 Filters can exclude components
filter := ecs.NewFilter2[Position, Velocity](&world).
Without(ecs.C[Altitude]())
_ = filter
API: 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()
_ = filter
API: Filter2.Exclusive
🔍 Filters can combine multiple conditions
filter := ecs.NewFilter1[Position](&world).
With(ecs.C[Velocity]()).
With(ecs.C[Altitude]()).
Without(ecs.C[Health]())
_ = filter
API: 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, vel
API: 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)
_ = hasPosAndVel
API: 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
Note
This feature is not yet released and is planned for Ark v0.6.0.
You can try it out on the main
branch.
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