Expand description
Lluvia - A stripped down Entity Component System that allows for no-nonsense data storage in finite time.
This library lets you quickly throw together large collections of objects
with varying lifetimes into one ECS. You specify the Components, create
any number of reference counted Entity objects, and when an Entity goes
out of scope its data will be automatically dropped as well. You can even
store Entity objects as components of other entities, and everything will
get dropped at once when the root Entity goes out of scope.
What sets this ECS apart is that it is very fast, very small in scope, and has a very small footprint. The implementation is ~1000 lines, and almost all operations run in O(1) time. There is no archetyping, there is no rayon integration, there is no advanced iterator pattern, and there are no dependencies. Emphasis is placed on minimizing complexity and avoiding scanning or re-organizing data, as Lluvia was designed to be the data engine for low-latency graphics programs.
Lluvia does support multi-threaded access. Entity is internally an
Arc<usize>, and each Component is a data table wrapped in a RwLock.
Accessing a piece of data involves going through the overhead of unlocking
the Component table, but data access is immediate. calling the get
and get_mut methods will return a TableRef, which internally holds
the RwLock open for as long as the reference is active.
The two main gotcha’s of using Lluvia are being aware of the locking
behavior from holding open references to component values, and preventing
circular references from placing Entitys inside of Components.
All data in Component tables is not dropped until the owning Entity
is dropped, and placing two Entitys in each other’s Components
causes that to never happen. As long as you are aware of these you can
get decent performance and never leak memory.
Lluvia begins with creating an Instance object. This will track the
validity of Entity objects in the system, and will hold references
to data tables used for storage.
The Instance can then be used to add Component tables. The Component
allows for getting and setting components for each Entity.
§Basic Usage
Basic usage looks like:
use lluvia as ll;
// Create the ECS holder
let mut inst = ll::Instance::new();
// Make a new entity
let entity = inst.add_entity();
// Now add our component. This will be a string, but
// we don't have to specify that for now
let mut c = inst.add_component();
// Before querying the value, we first need to set a valid value
// for this component. Afterwards, we can get it and check that
// it is unchanged.
c.set(&entity, "Hola Lluvia");
let data_ref = c.get(&entity).unwrap();
assert_eq!(*data_ref, "Hola Lluvia");§Sparse vs Non-Sparse Components
Most entities may not have values for every Component defined in the system.
In this case it doesn’t make sense to allocate an entire gigantic backing array
for only 10% of the entities to be defined. For this reason the default component
type in Lluvia is “Sparse”. Sparse components are non-contiguous and will only
allocate “blocks” of the backing store to service Entity usage. If only a few
entities set values for that component, only a few blocks are allocated.
Lluvia also handles the opposite scenario. Sometimes you really do need a contiguous array of data, and that is what the “non-sparse” component type is for. For this component type you must provide a default value and the backing array may waste memory, but it allows you to access the raw backing array if needed. Non-Sparse components are exceptionally useful for integrating with other libraries, for example keeping a list of window positions and initializing OpenGL vertex arrays from the raw backing store without having to replicate it.
§Snapshots
Snapshots are another advanced feature which allow you to update many Entity
values and then apply all the changes in one commit. Snapshots are a type of
Component, and only apply to one Sparse Component.
Structs§
- Component
Iterator - Component
List - Entity
Internal - Instance
- An Entity component system.
- Instance
Internal - RawComponent
- A Component holding values for each Entity
- Slice
Container - A continuous non-space slice container
- Slice
Ref - Helper struct for a slice
- Snapshot
- Snapshot Component
- Table
- Table
Internal - A table containing a series of optional values.
- Table
Ref - Table
RefMut - VecContainer
- Our basic vector storage
- VecContainer
Iter
Traits§
- Container
- The storage backend for a particular table
Type Aliases§
- Component
- General Purpose Component
- Entity
- An abstract Entity
- NonSparse
Component - Component with non-sparse data allocation