Advanced Topics
Docs / Guides / Advanced topics
Advanced patterns, optimizations, and architectural considerations for ZenECS.
Overview
This guide covers advanced topics for experienced ZenECS users:
- Performance Optimization: Hot path optimizations and profiling
- Multi-World Patterns: Complex world management scenarios
- Custom Extensions: Extending ZenECS with custom functionality
- Networking: Deterministic simulations for multiplayer
- Testing Strategies: Unit testing and integration testing
Performance Optimization
Query Optimization
Cache queries when possible:
public class MovementSystem : ISystem
{
// Cache query result
private List<(Entity, Position, Velocity)> _entities = new();
public void Run(IWorld world, float deltaTime)
{
_entities.Clear();
foreach (var item in world.Query<Position, Velocity>())
{
_entities.Add(item);
}
// Process cached list
foreach (var (entity, pos, vel) in _entities)
{
// Process
}
}
}
Filter early in the pipeline:
// ✅ Good: Filter early
foreach (var (entity, health) in world.Query<Health>()
.Where(e => world.Get<Health>(e).Current > 0))
{
// Only process alive entities
}
// ❌ Bad: Filter late
foreach (var (entity, health) in world.Query<Health>())
{
if (health.Current <= 0) continue; // Filtered too late
// Process
}
Component Pool Optimization
Pre-allocate pools for known component types:
var world = kernel.CreateWorld(new WorldConfig
{
InitialPoolBuckets = new Dictionary<Type, int>
{
{ typeof(Position), 1000 },
{ typeof(Velocity), 1000 },
{ typeof(Health), 500 }
}
});
System Execution Order
Optimize system order to minimize cache misses:
[FixedGroup]
[OrderBefore(typeof(RenderSystem))] // Run before expensive systems
public class MovementSystem : ISystem { }
Multi-World Patterns
Split-Screen Games
var kernel = new Kernel();
// Create separate worlds for each player
var player1World = kernel.CreateWorld(null, "Player1", tags: new[] { "player1" });
var player2World = kernel.CreateWorld(null, "Player2", tags: new[] { "player2" });
// Step worlds independently
kernel.PumpAndLateFrame(dt, fixedDelta, maxSubStepsPerFrame: 4);
Server/Client Separation
// Server world (authoritative)
var serverWorld = kernel.CreateWorld(null, "Server", tags: new[] { "server" });
// Client world (prediction)
var clientWorld = kernel.CreateWorld(null, "Client", tags: new[] { "client" });
// Sync server state to client
SyncWorlds(serverWorld, clientWorld);
Game Mode Isolation
// Menu world
var menuWorld = kernel.CreateWorld(null, "Menu");
// Gameplay world
var gameplayWorld = kernel.CreateWorld(null, "Gameplay");
// Switch between worlds
kernel.SetCurrentWorld(menuWorld); // Show menu
kernel.SetCurrentWorld(gameplayWorld); // Start game
Custom Extensions
Custom System Groups
Add new system execution phases:
// 1. Extend SystemGroup enum (internal)
public enum SystemGroup
{
// ... existing groups
CustomPhase
}
// 2. Add execution logic to SystemRunner
public void CustomPhase(IWorld world, float deltaTime)
{
RunGroup(SystemGroup.CustomPhase, world, deltaTime);
}
Custom Component Formatters
Implement custom serialization:
public class JsonComponentFormatter : IComponentFormatter
{
public void WriteComponent(Stream stream, Type componentType, object component)
{
var json = JsonUtility.ToJson(component);
var bytes = Encoding.UTF8.GetBytes(json);
stream.Write(bytes, 0, bytes.Length);
}
public object ReadComponent(Stream stream, Type componentType)
{
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
var json = Encoding.UTF8.GetString(bytes);
return JsonUtility.FromJson(json, componentType);
}
}
// Use custom formatter
world.Save(stream, new JsonComponentFormatter());
Custom Hooks
Add custom validation and permissions:
// Write permission hook
world.Hooks.AddWritePermission((entity, componentType) =>
{
// Custom logic
return HasPermission(entity, componentType);
});
// Value validator
world.Hooks.AddValidator<Health>(health =>
{
// Validate health values
return health.Current >= 0 && health.Current <= health.Max;
});
Networking Patterns
Deterministic Simulation
Ensure reproducible simulations:
// Use fixed timestep
const float fixedDelta = 1f / 60f;
// Deterministic random seed
var random = new System.Random(seed);
// Deterministic system order
[FixedGroup]
[OrderAfter(typeof(InputSystem))]
public class GameplaySystem : ISystem { }
State Synchronization
Sync world state between server and client:
// Server: Create snapshot
var snapshot = world.CreateSnapshot();
// Client: Apply snapshot
world.LoadSnapshot(snapshot);
Rollback and Prediction
Implement client-side prediction:
// Save state before prediction
var savedState = world.CreateSnapshot();
// Run prediction
world.Step(fixedDelta);
// If server state differs, rollback
if (ServerStateDiffers(savedState, serverState))
{
world.LoadSnapshot(savedState);
world.LoadSnapshot(serverState);
}
Testing Strategies
Unit Testing Systems
Test systems in isolation:
[Test]
public void MovementSystem_MovesEntities()
{
// Arrange
var world = new World(/* ... */);
var system = new MovementSystem();
Entity entity;
using (var cmd = world.BeginWrite())
{
entity = cmd.CreateEntity();
cmd.AddComponent(entity, new Position(0, 0));
cmd.AddComponent(entity, new Velocity(1, 0));
}
// Act
system.Run(world, 1f);
// Assert
var pos = world.Get<Position>(entity);
Assert.AreEqual(1f, pos.X);
Assert.AreEqual(0f, pos.Y);
}
Integration Testing
Test full world scenarios:
[Test]
public void World_CompleteGameplayLoop()
{
var kernel = new Kernel();
var world = kernel.CreateWorld(null, "Test");
// Setup
world.AddSystems([new MovementSystem(), new HealthSystem()]);
Entity player;
using (var cmd = world.BeginWrite())
{
player = cmd.CreateEntity();
cmd.AddComponent(player, new Position(0, 0));
cmd.AddComponent(player, new Health(100));
}
// Execute
kernel.PumpAndLateFrame(1f, 1f / 60f, maxSubStepsPerFrame: 4);
// Verify
Assert.IsTrue(world.IsAlive(player));
}
Best Practices
✅ Do
- Profile first: Identify bottlenecks before optimizing
- Use appropriate data structures: Choose components wisely
- Minimize allocations: Reuse collections, use structs
- Test determinism: Verify reproducible simulations
❌ Don't
- Don't over-optimize: Premature optimization is the root of all evil
- Don't break determinism: Avoid non-deterministic operations in fixed-step
- Don't mix concerns: Keep systems focused and isolated
See Also
- Performance Guide - Performance optimization details
- Architecture - System architecture
- Extending ZenECS - Extension patterns