ArchetypeBuilder¶
ArchetypeBuilder efficiently creates large numbers of entities with the same component layout. It pre-allocates all chunks at once and populates them in parallel, making it significantly faster than calling Registry::CreateEntity in a loop for bulk spawning.
Obtain a builder from the registry:
Bulk creation flow¶
graph TB
subgraph Setup["Builder Methods"]
WC["WithComponent<T>(default)"]
WV["WithComponent<T>(default)"]
WE["WithEntities(count)"]
FE["ForEach<T>(callback)"]
end
subgraph Process["Build Process"]
ALLOC["Pre-allocate chunks"]
REG["Register components on archetype"]
SPAWN["Create entities in bulk"]
CALL["Run ForEach callbacks"]
DONE["Return Ref<Archetype>"]
end
WC --> WV --> WE --> FE
FE --> ALLOC
ALLOC --> REG --> SPAWN --> CALL --> DONE Basic usage¶
registry->CreateArchetypeBuilder()
.WithComponent(Position { .x = 0.f, .y = 0.f })
.WithComponent(Velocity { .dx = 1.f })
.WithEntities(100'000)
.Build();
This creates 100,000 entities, each with a Position initialised to {0, 0} and a Velocity initialised to {dx: 1}. All entities share a single archetype.
Methods¶
WithComponent<T>(value)¶
Declares a component type and sets the default value applied to every entity created by this builder.
Signature:
Complexity: $O(1)$ — registers the component type on the archetype and stores the default value function.
Thread safety: Not thread-safe.
Template parameter T is inferred from the argument type — must satisfy fr::IsComponent.
Calling WithComponent with the same type more than once is ignored (the first registration wins).
WithEntities(count)¶
Sets the number of entities to create.
Signature: ArchetypeBuilder& WithEntities(Entity entityCount)
Complexity: $O(1)$ — stores the count.
Thread safety: Not thread-safe.
Calling Build() with count = 0 returns nullptr without creating any entities.
ForEach<Ts...>(fn)¶
Registers a post-creation callback to customise individual entities. The callback receives (Entity, Ts&...) for every entity after they are created.
Signature:
Complexity: $O(N)$ where N is the entity count — called once per entity during Build().
Thread safety: Not thread-safe — callbacks run during Build().
builder
.WithComponent(Position {})
.WithEntities(1000)
.ForEach<Position>([](fr::Entity e, Position& pos) {
pos.x = static_cast<float>(e) * 2.f; // spread entities along X
});
Multiple ForEach calls are executed in registration order.
Build()¶
Commits the builder, creates the entities, and returns a Ref<Archetype>.
Signature: Ref<Archetype> Build()
Complexity: $O(N + C)$ where N is entity count and C is chunk count. Pre-allocates all chunks, then populates entities and runs callbacks.
Thread safety: Not thread-safe.
Archetype reuse¶
If an archetype with the same component signature already exists (from a previous Build() call or from CreateEntity), the new entities are appended to the existing archetype rather than creating a new one.
auto a1 = registry->CreateArchetypeBuilder()
.WithComponent(Position { .x = 0.f })
.WithEntities(100)
.Build();
auto a2 = registry->CreateArchetypeBuilder()
.WithComponent(Position { .x = 999.f })
.WithEntities(50)
.Build();
assert(a1 == a2); // same archetype object
assert(a1->Count() == 150); // 150 total entities across both builds
Memory layout during bulk creation¶
graph TB
subgraph ArchetypeMem["Archetype [Position, Velocity]"]
direction TB
subgraph Chunk0["Chunk 0 (capacity 512)"]
P0["ComponentArray<Position><br/>[p0, p1, ..., p511]"]
V0["ComponentArray<Velocity><br/>[v0, v1, ..., v511]"]
E0["SparseSet<Entity><br/>[e0, e1, ..., e511]"]
end
subgraph Chunk1["Chunk 1 (capacity 512)"]
P1["ComponentArray<Position><br/>[p512, ..., p1023]"]
V1["ComponentArray<Velocity><br/>[v512, ..., v1023]"]
E1["SparseSet<Entity><br/>[e512, ..., e1023]"]
end
subgraph ChunkN["... (until 100,000 entities)"]
PN["..."]
end
end
Chunk0 --> P0 & V0 & E0
Chunk1 --> P1 & V1 & E1 For 100,000 entities with chunk capacity 512: ~196 chunks, each fully packed.
Full example: spawning a grid of enemies¶
// Spawn a 10×10 grid of enemies with unique positions and health values
registry->CreateArchetypeBuilder()
.WithComponent(Position {})
.WithComponent(Health { .max = 50, .current = 50 })
.WithComponent(EnemyTag {})
.WithEntities(100)
.ForEach<Position>([](fr::Entity e, Position& pos) {
pos.x = static_cast<float>(e % 10) * 5.f;
pos.y = static_cast<float>(e / 10) * 5.f;
})
.ForEach<Health>([](fr::Entity, Health& hp) {
hp.current = hp.max; // ensure full health on spawn
})
.Build();
When to use CreateEntity instead¶
| Scenario | Use |
|---|---|
| Bulk spawn at startup (100+ entities with same layout) | ArchetypeBuilder |
| One-off entity during gameplay | Registry::CreateEntity |
| Mixed component sets per entity | Registry::CreateEntity |
| Dynamic entity creation based on runtime data | Registry::CreateEntity |