Expand description
What is an Entity Component System?
An Entity Component System or ECS is very similar to a relational database like * SQL*. The
World
is the data store where game objects (also known as Entity
) live. An Entity
contains data or Component
s.
The ECS can efficiently query those components.
Give me all entities that have a position and velocity component, and then update the position based on the velocity.
type PosVelQuery = (Write<Pos>, Read<Vel>);
// ^^^^^ ^^^^
// Mutable Immutable
world.matcher::<All<PosVelQuery>>().for_each(|(pos, vel)|{
pos += vel;
})
Internals
// A Storage that contains `Pos`, `Vel`, `Health`.
(
[Pos1, Pos2, Pos3, .., PosN],
[Vel1, Vel2, Vel3, .., VelN],
[Health1, Health2, Health3, .., HealthN],
)
// A Storage that contains `Pos`, `Vel`.
(
[Pos1, Pos2, Pos3, .., PosM]
[Vel1, Vel2, Vel3, .., VelM]
)
Iteration is fully linear with the exception of jumping to different storages.
The iteration pattern from the query above would be
positions: [Pos1, Pos2, Pos3, .., PosN], [Pos1, Pos2, Pos3, .., PosM]
velocities: [Vel1, Vel2, Vel3, .., VelN], [Vel1, Vel2, Vel3, .., VelM]
^
Jump occours here
The jump is something like a chain of two iterators. We look at all the storages
that match specific query. If the query would be Write<Position>
, then we would
look for all the storages that contain a position array, extract the iterators and chain them
Every combination of components will be in a separate storage. This guarantees that iteration will always be linear.
Benchmarks
Getting started
Structs
Iterator
is used to end a borrow from a query like World::matcher
.World::matcher
, but this is not possible if both borrows would be mutable.
Instead we track active borrows at runtime. Multiple reads are allowed but read/write
and
write/write
are not.Traits
BuildStorage
is used to create different Storage
s at runtime. See also
AppendComponents
and World::append_components
Storage
won’t have any arrays or vectors when it is created. RegisterComponent
can
register or add those component arrays. See also EmptyStorage::register_component
Storage
allows to abstract over differnt types of storages. The most common storage that
implements this trait is SoaStorage
.