Batch Operations
Compared to Queries and World Entity Access, creation and removal of entities or components are relatively costly operations. See the Benchmarks for some numbers.
For these operations, Arche provides batched versions.
This allows to create or manipulate a large number of entities much faster than one by one.
Most batch methods come in two flavors. A “normal” one, and one suffixed with Q
that returns a query over the affected entities.
Creating entities
Entity creation is probably the most common use case for batching.
When the number of similar entities that are to be created are known,
creation can be batched with
ecs.Builder.NewBatch
.
In the generic API, MapX provide e.g.
generic.Map2.NewBatch
.
1world := ecs.NewWorld()
2
3builder := generic.NewMap2[Position, Heading](&world)
4
5builder.NewBatch(100)
1world := ecs.NewWorld()
2
3posID := ecs.ComponentID[Position](&world)
4headID := ecs.ComponentID[Heading](&world)
5
6builder := ecs.NewBuilder(&world, posID, headID)
7
8builder.NewBatch(100)
However, this is only sometimes useful, as we can’t initialize component fields here.
With the query variant of the methods, suffixed with Q
, we can fix this:
1world := ecs.NewWorld()
2
3builder := generic.NewMap2[Position, Heading](&world)
4
5query := builder.NewBatchQ(100)
6for query.Next() {
7 pos, head := query.Get()
8
9 pos.X = rand.Float64() * 100
10 pos.Y = rand.Float64() * 100
11 head.Angle = rand.Float64() * 360
12}
1world := ecs.NewWorld()
2
3posID := ecs.ComponentID[Position](&world)
4headID := ecs.ComponentID[Heading](&world)
5
6builder := ecs.NewBuilder(&world, posID, headID)
7
8query := builder.NewBatchQ(100)
9for query.Next() {
10 pos := (*Position)(query.Get(posID))
11 head := (*Heading)(query.Get(headID))
12
13 pos.X = rand.Float64() * 100
14 pos.Y = rand.Float64() * 100
15 head.Angle = rand.Float64() * 360
16}
Here, we obtain a query over exactly the entities we just created, and can initialize their components.
Components
Components can be added, removed or exchanged in batch operations.
For these operations, Arche provides
ecs.World.Batch
.
Component batch operations take an
ecs.Filter
as an argument to determine the affected entities.
1world := ecs.NewWorld()
2
3// Create 100 entities with Position.
4builder := generic.NewMap1[Position](&world)
5builder.NewBatch(100)
6
7// Create a generic map to perform the batch operation
8adder := generic.NewMap1[Heading](&world)
9// Filter for entities with Position.
10filter := generic.NewFilter1[Position]()
11// Batch-add Heading to them, using the query method for initialization.
12query := adder.AddBatchQ(filter.Filter(&world))
13for query.Next() {
14 head := query.Get()
15 head.Angle = rand.Float64() * 360
16}
1world := ecs.NewWorld()
2
3posID := ecs.ComponentID[Position](&world)
4headID := ecs.ComponentID[Heading](&world)
5
6// Create 100 entities with Position.
7builder := ecs.NewBuilder(&world, posID)
8builder.NewBatch(100)
9
10// Filter for entities with Position.
11filter := ecs.All(posID)
12// Batch-add Heading to them, using the query method for initialization.
13query := world.Batch().AddQ(filter, headID)
14for query.Next() {
15 head := (*Heading)(query.Get(headID))
16 head.Angle = rand.Float64() * 360
17}
Methods of interest for the ID-based API are:
-
ecs.Batch.Add
,ecs.Batch.AddQ
-
ecs.Batch.Remove
,ecs.Batch.RemoveQ
-
ecs.Batch.Exchange
,ecs.Batch.ExchangeQ
Methods of interest for the generic API are:
-
generic.Map2.AddBatch
,generic.Map2.AddBatchQ
-
generic.Map2.RemoveBatch
,generic.Map2.RemoveBatchQ
-
generic.Exchange.ExchangeBatch
Relations
Entity relations can be changed in batches, too.
In the ID-based API, both
ecs.Batch.SetRelation
/
ecs.Batch.SetRelationQ
and
ecs.Relations.SetBatch
/
ecs.Relations.SetBatchQ
can be used.
In the generic API, use
generic.Map.SetRelation
/
generic.Map.SetRelationQ
:
1world := ecs.NewWorld()
2
3childID := ecs.ComponentID[ChildOf](&world)
4
5parent := world.NewEntity()
6
7// Create 100 entities with ChildOf relation.
8builder := generic.NewMap1[ChildOf](&world)
9builder.NewBatch(100)
10
11// Create a generic map to perform the batch operation
12mapper := generic.NewMap[ChildOf](&world)
13// Filter for entities with ChildOf.
14filter := ecs.All(childID)
15// Batch-set their relation target to parent.
16mapper.SetRelationBatch(filter, parent)
1world := ecs.NewWorld()
2
3childID := ecs.ComponentID[ChildOf](&world)
4
5parent := world.NewEntity()
6
7// Create 100 entities with ChildOf relation.
8builder := ecs.NewBuilder(&world, childID)
9builder.NewBatch(100)
10
11// Filter for entities with ChildOf.
12filter := ecs.All(childID)
13// Batch-set their relation target to parent.
14world.Batch().SetRelation(filter, childID, parent)
Removing entities
Entities can be removed in batches using
ecs.Batch.RemoveEntities
:
1world := ecs.NewWorld()
2
3// Create 100 entities with Position.
4builder := generic.NewMap1[Position](&world)
5builder.NewBatch(100)
6
7// Filter for entities with Position.
8filter := generic.NewFilter1[Position]()
9// Batch-remove matching entities.
10world.Batch().RemoveEntities(filter.Filter(&world))
1world := ecs.NewWorld()
2
3posID := ecs.ComponentID[Position](&world)
4
5// Create 100 entities with Position.
6builder := ecs.NewBuilder(&world, posID)
7builder.NewBatch(100)
8
9// Filter for entities with Position.
10filter := ecs.All(posID)
11// Batch-remove matching entities.
12world.Batch().RemoveEntities(filter)