Batch operations
In an archetype-based ECS, creation and removal of entities or components are relatively costly operations. For these operations, Ark provides batched versions. This allows to create or manipulate a large number of entities much faster than one by one. All batch methods come in two flavors. A "normal" one, and one that runs a callback function on the affected entities.
Creating entities
Often, multiple entities with the same set of components are created at the same time. Batch entity creation is therefore probably the most frequently used batch operation. There are different ways to create entities in batches:
From default component values using new_entities!. Here, we create 100 entities, all with the same Position and Velocity:
new_entities!(world, 100, (
Position(100, 100),
Velocity(0, 0),
))This may be sufficient in some use cases, but most often we will use a second approach:
From component types with subsequent manual initialization using new_entities! with a tuple of types:
new_entities!(world, 100, (Position, Velocity)) do (entities, positions, velocities)
for i in eachindex(entities)
positions[i] = Position(i, i)
velocities[i] = Velocity(0, 0)
end
endNote that the tuple elements of the callback argument are entity and component columns that need to be iterated to access individual items. See also the chapter on Queries, which use a similar nested loop structure.
Note that with the second approach, all components of all entities should be set as they are otherwise uninitialized. Therefore, the callback is mandatory here, while it is optional for batch creation from default values.
Removing entities
Similar to entity creation, entities can also be removed in batches with remove_entities!. It takes a Filter instead of a single entity as argument:
filter = Filter(world, (Position, Velocity))
remove_entities!(world, filter)If something needs to be done with the entities to be removed, a callback can be used, which takes an Entities column as an argument:
filter = Filter(world, (Position, Velocity))
remove_entities!(world, filter) do entities
# do something with the entities...
endAdding and removing components
The functions add_components!, remove_components! and exchange_components! also come with batch versions.
Similarly to batch entity creation, components to be added can be given either in the form of default values, or as types. In the case of default values, usage of the callback is optional, while it is mandatory for initialization with the types version.
Here, we add a default Velocity component to all entities with Position, using add_components!:
filter = Filter(world, (Position,))
add_components!(world, filter, (Velocity(0, 0),))Adding components via types, with individual initialization:
filter = Filter(world, (Position,))
add_components!(world, filter, (Velocity,)) do (entities, velocities)
for i in eachindex(entities, velocities)
velocities[i] = Velocity(randn(), randn())
end
endNote that the tuple elements of the callback argument are entity and component columns that need to be iterated to access individual items. See also the chapter on Queries, which use a similar nested loop structure.
Removing components works in a similar way, with remove_components!:
filter = Filter(world, (Velocity,))
remove_components!(world, filter, (Velocity,))Note that the optional callback has only an Entities column as argument:
filter = Filter(world, (Velocity,))
remove_components!(world, filter, (Velocity,)) do entities
# do something with the entities...
endFinally, exchanging components with exchange_components! follows the same pattern as adding components:
filter = Filter(world, (Velocity,))
exchange_components!(world, filter;
add=(Health(100),),
remove=(Velocity,),
)Note that, again, when adding components as types, the callback is mandatory. Also note that only the added components are part of the callback's argument tuple, while the removed components are not.
filter = Filter(world, (Velocity,))
exchange_components!(world, filter;
add=(Health,),
remove=(Velocity,),
) do (entities, healths)
for i in eachindex(entities, healths)
healths[i] = Health(i * 2)
end
endSetting relationships
As with other operations, relation targets can be set in batches using set_relations! combined with a Filter:
filter = Filter(world, (ChildOf,); relations=(ChildOf => parent,))
set_relations!(world, filter, (ChildOf => parent2,))If necessary, the affected entities can be processed using a callback function:
filter = Filter(world, (ChildOf,); relations=(ChildOf => parent,))
set_relations!(world, filter, (ChildOf => parent2,)) do entities
# do something with the entities...
end