Dynamic components

A component is nothing more than a type safe entity id.

The component uses a lazily acquired entity. It does not require the world since the entity is spawned in the STATIC global namespace which is shared across all worlds.

It is entirely possible to create a component at runtime for e.g; a local system.

#![allow(unused)]

fn main() {
    registry().with(HierarchicalLayer::default()).init();

    let mut world = World::new();

    let position: Component<Vec2> = world.spawn_component("position", |desc| {
        let mut buf = ComponentBuffer::new();
        <Debug as MetaData<Vec2>>::attach(desc, &mut buf);
        buf
    });

    let id = Entity::builder()
        .set(position, vec2(1.0, 6.4))
        .spawn(&mut world);

    tracing::info!("world: {world:#?}");

    // When `position` is despawned, it is removed from all entities.
    // This ensured that dead components never exist
    world.despawn(position.id())?;

    tracing::info!("world: {world:#?}");

    world.despawn(id)?;
}

The meta allows the user to provide a function to attach extra components to the entity. This is used by the => [ty, ty] syntax for the component macro.

The world will automatically manage the lifetime of the component to ensure that no entity has invalid components attached to it.

Relations

As a component is just a normal entity you can add an entity to another entity. Such as adding a parent entity to the children entities.

However, while this method allows quick and easy entity hierarchies, there is no notion of what the entity represents, and no way to distinguish it from another component.

This is where component parameterization comes to play.

An entity is 64 bits in size, sufficient for holding the kind, index, and generation.

However, since the world manages the lifetime of the component, the generation of 32 bits is freed up, which allows other data to be stored, such as another entity id.

This allows the upper bits of a component(entity) id to contain another entity as the generation is not needed.

#![allow(unused)]

fn main() {
    #[derive(Debug, Clone)]
    struct RelationData {
        // This allows you to add extra data to the child in the relation
        distance: f32,
    }

    let child_of = world.spawn_relation::<RelationData>("child_of", |desc| {
        let mut buf = ComponentBuffer::new();
        <Debug as MetaData<RelationData>>::attach(desc, &mut buf);
        buf
    });

    let parent = world.spawn();

    let child = Entity::builder()
        .set(child_of(parent), RelationData { distance: 1.0 })
        .spawn(&mut world);

    let data = world.get(child, child_of(parent))?;

    tracing::info!("Relation distance: {:?}", data.distance);

    drop(data);

    world.despawn(parent)?;
    assert!(world.get(child, child_of(parent)).is_err());

}

When despawning either the relation component or target entity, the "parent", the component is removed from all entities.