Entities, components and the world
Larecs🌲 is an entity component system (ECS) framework for Mojo. The ECS concept is composed of three principal elements:
- Entities represent game objects or simulation entities, like individuals in a population model.
- Components are the data associated to entities, i.e. their properties or state variables.
- Systems contain the game or simulation logic that manipulates entities and their components, using so-called queries.
In an ECS, each entity is “composed of” an arbitrary set of components that can be added and removed at run-time. This modular design enables the development of highly flexible and reusable games or simulations. By decoupling the logic (systems) from the data (entities and components), ECS avoids convoluted inheritance hierarchies and eliminates hard-coded behavior. Instead, entities can be composed of components to exhibit diverse behaviors without sacrificing modularity.
An ECS engine manages this architecture within a central storage structure known as the “World”. The engine handles common tasks such as maintaining entity lists, spawning or deleting entities, and scheduling logic operations, simplifying the development process for users.
Larecs🌲 provides a high-performance ECS framework, empowering developers to create games and simulation models with exceptional flexibility and efficiency.
Entities
Entities are the central unit around which the data in
an ECS is organized. Entities could represent game objects,
or potentially anything else that “lives” in a game or simulation.
Each entity can possess an arbitrary
set of components, which can also be used to characterize
an entity. That is, in an ECS, cars would not be directly
identified as “cars” but rather as something that has
the components Position
, Velocity
, Fuel reserves
,
Engine power
, etc.
The components are not stored in individual
objects but rather in a central container, called the World
.
Hence, an Entity
is merely an identifier that allows
retrieving the corresponding components from the world.
As such, entities are strictly bound to the world they live in,
but also small in memory and easy to store.
Note
Though entities can safely be stored and passed around, their components (or pointers to them) should never be stored externally, as they can move in memory at any time.
How entities are created and used is discussed in the next chapters.
Components
Components are the data associated with entities, characterizing
their state and properties. Components are represented
via structs: each component type is a different struct.
For example, if we want to model a world in which
entities may have a position and a velocity, we need to
define a Position
and a Velocity
struct.
@value
struct Position:
var x: Float64
var y: Float64
@value
struct Velocity:
var dx: Float64
var dy: Float64
These structs are not only used to store data. Their types are also used as unique identifiers for the components of an entity. That is, they have a similar role to a key in a dictionary.
How components can be assigned to entities is described in the following chapters.
Warning
Currently, only “trivial” structs are supported as
components in Larecs🌲. That is, structs that can be
copied and moved via simple memory operations. This does
not include structs that manage heap-allocated memory
such as List
or Dict
. Typically, it is not advisable to
use such objects in the ECS context anyway; however,
Larecs🌲 might support such “complex” structs in a future version.
The world
The central data structure of Larecs🌲 is the
World
. It stores all data and information about
the state of the entities and their surroundings.
The World
struct provides the main functionality
of the ECS, such as adding and removing entities,
looking up components, and iterating over entities.
Larecs🌲 gains efficiency and usability by exploiting
Mojo’s compile-time programming capabilities.
To that end, it needs to know ahead of time
which components might turn up in the world, and the World
must be statically parameterized with the component types upon
creation. This also has the advantage that certain errors
can already be prevented at compile time, which makes the
program safer and faster.
To set up a World
, simply import it from the larecs
package
and create a World
instance as follows:
from larecs import World
def main():
# Create a world with the components Position and Velocity
world = World[Position, Velocity]()