std/ecs/entity

std/ecs/src/entity.trb

The handle components are attached to, and the allocator that hands handles out.

type Entity

type Entity with Equals, Hash, Show

What components are attached to: an index into the columns and the generation of the handle.

Examples

const handle = Entity 3
assert(handle == Entity(3, 0))
assert(handle.generation == 0)

field index

index: Int

The slot the entity occupies. Handed out again after a despawn, with a higher generation.

field generation

generation: Int = 0

Counts up every time index is handed out again, so a stale handle is detectable.

type Entities

type Entities with Equals, Show

The allocator of Entity handles, and the only place where a generation changes.

An index that was freed comes back before a new one is made - the one freed last first - and with its generation one higher, so the handle that was kept across the despawn no longer matches. The same sequence of operations gives the same handles, which is what a lockstep simulation needs (ECS.md, "Determinism").

Entities does not know a column. Taking an entity's components out when it is despawned is the world's job, in the program that owns the columns.

Examples

var entities = Entities()
const first = entities.spawn()
const second = entities.spawn()
assert entities.despawn(first)
assert(!entities.isAlive(first))
const reused = entities.spawn()
assert(reused == Entity(0, 1))
assert(entities.length() == 2)
assert entities.isAlive(second)

Pitfalls

  • A handle that was despawned stays a valid value: isAlive is how a caller asks, and a column answers None for it only because the program took its components out.

field generations

protected var generations: List<Int> = []

The generation the next handle of each index gets: raised by the despawn of the one before.

field living

protected var living: List<Bool> = []

Whether each index is handed out right now.

field freed

protected var freed: List<Int> = []

The indices that were despawned, the one freed last at the end.

field live

protected var live: Int = 0

How many entities are alive.

fn spawn

var fn spawn(): Entity

Hands out an entity: the index freed last with its generation raised, or else the next new index.

fn despawn

var fn despawn(entity: Entity): Bool

Retires an entity and answers true. A handle that is stale, never handed out or already despawned changes nothing and answers false.

fn isAlive

fn isAlive(entity: Entity): Bool

Whether entity is the handle currently handed out for its index.

fn length

fn length(): Int

How many entities are alive.