Entity Builder

The EntityBuilder allows you to incrementally construct an entity by adding components and then inserting it into the world. This provides both better ergonomics and efficiency as the entity does not bounce around archetypes.

Additionally, the entity builder allows constructing an entity in the absence of the world, like a function which only creates the EntityBuilder.

#![allow(unused)]
fn main() {
    component! {
        health: f32 => [Debuggable],
        position: (f32, f32) => [Debuggable],
        is_player: () => [Debuggable],
    }

    let mut world = World::new();

    // Instead of this
    let player = world.spawn();
    world.set(player, health(), 100.0).unwrap();
    world.set(player, position(), (5.0, 2.3)).unwrap();
    world.set(player, is_player(), ()).unwrap();
    world.set(player, name(), "Player".into()).unwrap();

    tracing::info!("Player: {:#?}", world.format_entities(&[player]));
    world.despawn(player).unwrap();

    // Do this
    let player = Entity::builder()
        .set(health(), 100.0)
        .set(position(), (5.0, 2.3))
        .tag(is_player())
        .set(name(), "Player".into())
        .spawn(&mut world);

    tracing::info!("Player: {:#?}", world.format_entities(&[player]));

}

When the entity builder is spawned, the held components are moved out into the matching archetype in the world.

This means the entity builder is cleared and ready to insert more components.

#![allow(unused)]

fn main() {
    let mut builder = Entity::builder();
    let mut rng = StdRng::seed_from_u64(42);

    let enemies = (0..10)
        .map(|i| {
            builder
                .set(health(), rng.gen_range(50..100) as f32)
                .set(
                    position(),
                    (rng.gen_range(-10.0..10.0), rng.gen_range(-10.0..10.0)),
                )
                .set(name(), format!("Enemy.{i}"))
                .spawn(&mut world)
        })
        .collect_vec();

    tracing::info!("Enemies: {enemies:?}");
}
#![allow(unused)]

fn main() {
    let mut query = Query::new((name(), position(), is_player().opt(), health()));
    for (name, pos, is_player, health) in &mut query.borrow(&world) {
        tracing::info!("name: {name}, pos: {pos:?}, player: {is_player:?}, health: {health}");
    }

    // Or to only get the non players

    {
        let mut query = Query::new((name(), position(), health())).without(is_player());
        info_span!("enemies");
        for (name, pos, health) in &mut query.borrow(&world) {
            tracing::info!("name: {name}, pos: {pos:?}, health: {health}");
        }
    }

}

Batch Spawn

If inserting many entities of the same type, it is more efficient to let the world know about the coming components and insert everything at once.

The batch spawn allows spawning many entities with the same component types at once by inserting columns of each component type. This is akin to the native format of the archetype storage and is thus maximally efficient.

#![allow(unused)]

fn main() {
    let mut trees = BatchSpawn::new(10000);
    trees
        .set(name(), (0..).map(|i| format!("Tree.{i}")))
        .expect("Invalid length");

    trees
        .set(
            position(),
            (0..).map(|i| {
                let f = i as f32 / 32.0;
                (f.cos() * (1.0 + f / 2.0), f.sin() * (1.0 + f / 2.0))
            }),
        )
        .expect("Invalid length");

    let trees = trees.spawn(&mut world);

    tracing::info!("Trees: {:#?}", world.format_entities(&trees[0..100]));

}

Hierarchy

In the previous chapter, hierarchies were constructed by adding a relation to an entity to the child entity.

The entity builder allows construction of hierarchies before being spawned into the world.

#![allow(unused)]

fn main() {
    let id = Entity::builder()
        .set(name(), "parent".into())
        .attach(
            child_of,
            Entity::builder()
                .set(name(), "child1".into())
                .attach(child_of, Entity::builder().set(name(), "child1.1".into())),
        )
        .attach(child_of, Entity::builder().set(name(), "child2".into()))
        .spawn(&mut world);

    tracing::info!("Parent: {id}");

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

}