std/ecs/column
std/ecs/src/column.trb
type Column
type Column<Component>
One component type's dense column: the values in insertion order, the entity each row belongs to, and the row each entity occupies.
A column is where a component lives, and nothing in it knows what the component is. Every member is generic over
Component, so each one is monomorphized for the type it holds and no value is ever kept under a type that was
forgotten. Every write stamps its row from a clock the column keeps, which is all change detection is
(changedSince).
Examples
var names: Column<String> = Column()
const first = Entity 0
const second = Entity 1
names.put first, "ada"
names.put second, "grace"
assert(names.get(second) == Some("grace"))
names.remove first
assert(names.length() == 1)
assert(names.ownerAt(0) == second)
Pitfalls
- Removing swaps the last row into the hole, so a system must not remember a row number across a
remove. - A column answers for the entity handle it was given and knows nothing about Entities: a despawned entity is gone from a column only because the program took it out.
field owners
protected var owners: List<Entity> = []
The entity each row belongs to.
field values
protected var values: List<Component> = []
The values, dense and in insertion order.
field rows
protected var rows: Map<Entity, Int> = [:]
The row each entity occupies.
field stamps
protected var stamps: List<Int> = []
What the clock stood at when each row was last written.
field clock
protected var clock: Int = 0
Counts the writes.
fn put
var fn put(entity: Entity, value: Component)
Puts value under entity, replacing whatever was there. A new entity gets the row after the last one.
fn remove
var fn remove(entity: Entity)
Takes entity's value out, swapping the last row into the hole. An entity without a value changes nothing.
fn each
var fn each(body: (var value: Component) => Void)
Changes every value in place, in row order. The rows it visits count as written: they are stamped with one tick of the clock, as Bevy marks mutable access.
fn get
fn get(entity: Entity): Component?
The value of entity, or None when it has none.
fn has
fn has(entity: Entity): Bool
Whether entity has a value in this column.
fn length
fn length(): Int
How many rows there are.
fn ownerAt
fn ownerAt(row: Int): Entity
The entity row row belongs to. It panics outside 0..length().
fn valueAt
fn valueAt(row: Int): Component
The value in row row. It panics outside 0..length().
fn version
fn version(): Int
What the clock stood at after the last write of any row.
fn changedSince
fn changedSince(stamp: Int): Iterate<Entity>
The entities whose value was written after stamp, in row order. Change detection, without a second data
structure: a system remembers the version it last looked at and asks for what is newer.
Examples
var counts: Column<Int> = Column()
counts.put Entity(0), 1
const seen = counts.version()
counts.put Entity(1), 2
assert(counts.changedSince(seen).toList() == [Entity(1)])