Flax
Flax is a performant and easy to use Entity Component System.
The world is organized by simple identifiers known as an Entity, which can have any number of components attached to them.
Systems operate and iterate upon entities and their attached components and provide the application logic.
Features
- Queries
- Change detection
- Query filtering
- System execution
- Multithreaded system execution through
Schedule - Builtin many to many entity relation and graphs
- Reflection through component metadata
- Ergonomic entity builder
- Tracing
- Serialization and deserialization
- Runtime components
Consider reading the User Guide
For those of you who prefer a more complete example, consider checking out an asteroid game made by flax and macroquad here
Example Usage
// Declare static components
use *;
component!
let mut world = new;
// Spawn an entity
let p = new
.set
.tag
.set
.set
.set_default
.spawn;
let mut query = new;
// Apply health regen for all match entites
for in &mut query.borrow
Systems
Queries with logic can be abstracted into a system, and multiple systems can be collected into a schedule.
let regen_system = builder
.with
.for_each
.boxed;
let despawn_system = builder
.with
.
.build
.boxed;
let mut schedule = from;
schedule.execute_par?;
Relations
Flax provides first class many-many relations between entities, which is useful for tree scene hierarchies, graphs, and physics joints between entities.
Relations can be both state-less or have associated data, like spring or joint strengths.
Relations are cache friendly and querying children of does not require random access. In addition, relations are cleaned up on despawns and are stable during serialization, even if the entity ids migrate due to collisions.
See the guide for more details.
component!
let mut world = new;
let parent = builder
.set
.spawn;
let child1 = builder
.set
.set_default
.spawn;
Comparison to other ECS
Compared to other ecs implementations, a component is simply another Entity
identifier to which data is attached. This means the same "type" can be added to
an entity multiple times.
A limitation of existing implementations such as specs, planck, or hecs is that newtype wrappers need to be created to allow components of the same inner type to coexist.
This leads to having to forward all trait implementations trough e.g
derive-more or dereferencing the newtypes during usage.
This can lead to situations such as this:
;
;
let vel = world.;
let mut pos = world.;
let dt = 0.1;
*pos = Position;
Which in Flax is:
component!
let vel = world.get?;
let mut pos = world.get_mut?;
let dt = 0.1;
*pos = *pos + *vel * dt;
On a further note, since the components have to be declared beforehand (not
always true, more on that later), it limits the amount of types which can be
inserted as components. This fixes subtle bugs which come by having the type
dictate the component, such as inserting an Arc<Type> instead of just Type,
which leads to subsequent systems not finding the Type on the entity.
Having statically declared componenents makes the rust type system disallow these cases and catches these bugs earlier.
Motivation
During development of a game in school I used the hecs ECS. It is an awesome
library, and the author Ralith has been awesome in bringing some pull
requests in.
Despite this, I often made subtle bugs with similar types. The game engine was
cluttered with gigantic newtypes for Velocity, Position with many deref
coercions in order to coexist.
Unsafe
This library makes use of unsafe for type erasure and the allocation in storage of ComponentBuffers and Archetypes.
License: MIT