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:

Methods of interest for the generic API are:

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)