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:#?}"); }