Prefabs
Prefabs are reusable entity templates that define a set of components. They allow you to define entity archetypes once and instantiate them multiple times with consistent component configurations.
Basic Usage
Defining a Prefab
Create a prefab using the fluent builder pattern:
var enemyPrefab = new EntityPrefab()
.With(new Position { X = 0, Y = 0 })
.With(new Health { Current = 100, Max = 100 })
.With(new Velocity { X = 0, Y = 0 })
.WithTag<EnemyTag>();
Registering a Prefab
Register prefabs with the world using a unique name:
world.RegisterPrefab("Enemy", enemyPrefab);
Spawning from a Prefab
Create entities from registered prefabs:
// Spawn with default prefab values
var enemy1 = world.SpawnFromPrefab("Enemy").Build();
// Spawn multiple instances
var enemy2 = world.SpawnFromPrefab("Enemy").Build();
var enemy3 = world.SpawnFromPrefab("Enemy").Build();
Customizing Spawned Entities
Override prefab components when spawning:
// Spawn with custom position
var enemy = world.SpawnFromPrefab("Enemy")
.With(new Position { X = 100, Y = 50 })
.Build();
// Add additional components not in the prefab
var bossEnemy = world.SpawnFromPrefab("Enemy")
.With(new BossMarker())
.With(new Health { Current = 500, Max = 500 }) // Override health
.Build();
Prefab Inheritance
Prefabs support inheritance, allowing derived prefabs to extend or override base prefab components.
Creating Derived Prefabs
// Base enemy prefab
var baseEnemyPrefab = new EntityPrefab()
.With(new Position { X = 0, Y = 0 })
.With(new Health { Current = 100, Max = 100 })
.WithTag<EnemyTag>();
world.RegisterPrefab("Enemy", baseEnemyPrefab);
// Flying enemy extends base enemy
var flyingEnemyPrefab = new EntityPrefab()
.Extends("Enemy") // Inherit from base
.With(new Velocity { X = 0, Y = -5 }) // Add new component
.WithTag<FlyingTag>(); // Add new tag
world.RegisterPrefab("FlyingEnemy", flyingEnemyPrefab);
// Boss enemy extends base with overridden health
var bossEnemyPrefab = new EntityPrefab()
.Extends("Enemy")
.With(new Health { Current = 500, Max = 500 }) // Override base health
.WithTag<BossTag>();
world.RegisterPrefab("BossEnemy", bossEnemyPrefab);
Inheritance Rules
- Component Merging: Derived prefabs include all components from the base prefab
- Component Override: Components of the same type in the derived prefab replace base components
- Tag Accumulation: Tags from both base and derived prefabs are included
- Resolution Order: Base prefabs are processed first, then derived prefab components are applied
Named Entity Spawning
Spawn named entities from prefabs for later retrieval:
// Spawn a named entity from a prefab
var player = world.SpawnFromPrefab("Player", "MainPlayer").Build();
// Later, retrieve by name
var foundPlayer = world.GetEntityByName("MainPlayer");
if (foundPlayer.IsValid)
{
ref var pos = ref world.Get<Position>(foundPlayer);
// Work with the player entity
}
Prefab Management
Checking Prefab Registration
if (world.HasPrefab("Enemy"))
{
var enemy = world.SpawnFromPrefab("Enemy").Build();
}
Unregistering Prefabs
// Remove a prefab (existing entities are unaffected)
bool removed = world.UnregisterPrefab("Enemy");
Listing All Prefabs
foreach (var prefabName in world.GetAllPrefabNames())
{
Console.WriteLine($"Registered prefab: {prefabName}");
}
Use Cases
Game Entity Templates
// Define common game entities
var playerPrefab = new EntityPrefab()
.With(new Position { X = 0, Y = 0 })
.With(new Health { Current = 100, Max = 100 })
.With(new Inventory { Slots = 20 })
.WithTag<PlayerTag>();
var itemPrefab = new EntityPrefab()
.With(new Position { X = 0, Y = 0 })
.With(new ItemData { Value = 10 })
.WithTag<PickupableTag>();
world.RegisterPrefab("Player", playerPrefab);
world.RegisterPrefab("Item", itemPrefab);
Level Loading
// Load level data and spawn entities from prefabs
foreach (var entityData in levelData.Entities)
{
world.SpawnFromPrefab(entityData.PrefabName)
.With(new Position { X = entityData.X, Y = entityData.Y })
.Build();
}
Pooling Patterns
// Pre-register prefabs at startup
void InitializePrefabs(World world)
{
world.RegisterPrefab("Bullet", new EntityPrefab()
.With(new Position())
.With(new Velocity())
.With(new Damage { Amount = 10 })
.WithTag<BulletTag>());
world.RegisterPrefab("Explosion", new EntityPrefab()
.With(new Position())
.With(new ParticleEffect { Type = EffectType.Explosion })
.With(new Lifetime { Remaining = 1.0f }));
}
// Spawn from prefabs during gameplay
Entity SpawnBullet(World world, Position pos, Velocity vel)
{
return world.SpawnFromPrefab("Bullet")
.With(pos)
.With(vel)
.Build();
}
Error Handling
Missing Prefab
try
{
var entity = world.SpawnFromPrefab("NonExistent").Build();
}
catch (InvalidOperationException ex)
{
// "No prefab registered with name 'NonExistent'."
}
Circular Inheritance
// This will throw when spawning
var prefabA = new EntityPrefab().Extends("B");
var prefabB = new EntityPrefab().Extends("A");
world.RegisterPrefab("A", prefabA);
world.RegisterPrefab("B", prefabB);
try
{
world.SpawnFromPrefab("A").Build(); // Throws InvalidOperationException
}
catch (InvalidOperationException ex)
{
// "Circular inheritance detected in prefab 'A'..."
}
Performance Considerations
- Registration: O(1) - Prefabs are stored by name in a dictionary
- Spawning: O(C * D) where C is total components and D is inheritance depth
- Inheritance Resolution: Resolved at spawn time, not registration time
Prefabs are designed for convenience and maintainability rather than hot-path performance. For performance-critical spawning scenarios with thousands of entities per frame, consider direct entity creation with world.Spawn().