Engine Systems Research Roadmap
This document outlines game engine systems that require further research and architectural planning before implementation. Each section describes the system's purpose, why it matters, potential approaches, and open questions to resolve.
Table of Contents
Pathfinding & Navigation
Why It Matters
The AI system (planned in #426-427) handles decision making - choosing what to do. But AI agents also need to move through the world, which requires:
- Finding paths around obstacles
- Navigating complex 3D geometry
- Handling dynamic obstacles (doors, moving platforms)
- Supporting different movement capabilities (flying, swimming, climbing)
Without pathfinding, AI is limited to:
- Direct movement toward targets (gets stuck on walls)
- Pre-scripted patrol routes
- Simple steering behaviors
Current State
- KeenEyes.Spatial provides broadphase queries (nearby entities)
- KeenEyes.Physics provides raycasting and collision
- No navigation mesh or pathfinding algorithm exists
Potential Approaches
Option A: Grid-Based A*
Pros: Simple, works well for 2D and tile-based games Cons: Memory-intensive for large worlds, not ideal for 3D
KeenEyes.Navigation/
├── Grid/
│ ├── NavigationGrid.cs # 2D/3D grid representation
│ ├── AStarPathfinder.cs # A* implementation
│ └── GridObstacle.cs # Component to mark blocked cells
Option B: Navigation Mesh (NavMesh)
Pros: Industry standard, efficient for 3D, supports agent sizes Cons: Complex to generate, requires baking step
KeenEyes.Navigation/
├── NavMesh/
│ ├── NavigationMesh.cs # Polygon mesh for walkable areas
│ ├── NavMeshAgent.cs # Component for pathfinding entities
│ ├── NavMeshObstacle.cs # Dynamic obstacles
│ └── NavMeshBuilder.cs # Mesh generation from geometry
├── Pathfinding/
│ ├── AStarOnNavMesh.cs # A* over navmesh polygons
│ └── FunnelAlgorithm.cs # Path smoothing
Option C: Hierarchical Pathfinding (HPA*)
Pros: Scales to very large worlds, fast long-distance paths Cons: More complex, requires preprocessing
Option D: Flow Fields
Pros: Excellent for many units going to same destination (RTS) Cons: Not ideal for individual agent paths
Integration with AI
// AI decides to move to target
public class ChaseAction : IAIAction
{
public BTNodeState Execute(Entity entity, Blackboard bb, IWorld world)
{
var target = bb.Get<Entity>("Target");
var targetPos = world.Get<Transform3D>(target).Position;
// Request path from navigation system
var nav = world.GetExtension<INavigationContext>();
var path = nav.FindPath(entity, targetPos);
if (path == null) return BTNodeState.Failure;
// Store path for movement system to follow
bb.Set("CurrentPath", path);
return BTNodeState.Success;
}
}
Open Questions
- 2D vs 3D focus? Should we support both, or prioritize one?
- NavMesh generation - Runtime or editor-time baking?
- Dynamic obstacles - How to handle doors, destructibles?
- Agent variety - Different sizes, flying units, vehicles?
- Integration pattern - Standalone plugin or part of AI?
- Recast/Detour - Use established library or build custom?
Recommendation
Start with NavMesh approach as it's the industry standard and works for most 3D games. Consider wrapping Recast Navigation for the heavy lifting, with KeenEyes-native components and systems on top.
Scene Management
Why It Matters
Currently, a KeenEyes World contains all entities. This works for small games, but larger games need:
- Level transitions - Moving between distinct areas
- Additive loading - Loading additional content without destroying current
- Streaming - Loading/unloading chunks as player moves
- Prefab instantiation - Spawning pre-designed entity groups
Current State
- KeenEyes.Persistence handles save/load of world state
- Prefabs sample demonstrates entity templates
- No scene abstraction or loading system exists
What is a "Scene"?
This is the key architectural question. Options:
Option A: Scene = World
Each scene is a separate World instance. Transitioning means disposing old World, creating new.
Pros: Clean isolation, simple mental model Cons: Can't share entities between scenes, expensive transitions
Option B: Scene = Entity Subset
Scenes are collections of entities within a single World. Loading adds entities, unloading removes them.
Pros: Entities can persist across scenes (player), additive loading natural Cons: More complex tracking, potential for leaks
Option C: Scene = Serialized Snapshot
Scenes are data files (like prefabs but larger). Loading deserializes into current World.
Pros: Works with existing persistence, clear data format Cons: Similar to Option B complexity
Potential Structure
KeenEyes.Scenes/
├── Core/
│ ├── Scene.cs # Scene definition (entity list or snapshot)
│ ├── SceneHandle.cs # Reference to loaded scene
│ └── SceneState.cs # Loading, Loaded, Unloading
├── Loading/
│ ├── ISceneLoader.cs # Load scene data
│ ├── SceneLoadRequest.cs # Async load request
│ └── SceneManager.cs # Orchestrates loading/unloading
├── Streaming/
│ ├── StreamingVolume.cs # Component triggering load/unload
│ ├── StreamingSystem.cs # Monitor player position
│ └── ChunkManager.cs # Grid-based streaming
└── Transitions/
├── SceneTransition.cs # Fade, crossfade, etc.
└── LoadingScreen.cs # UI during loads
Integration with Assets
Scene loading should use the Asset Management system:
var assets = world.GetExtension<AssetManager>();
var sceneHandle = await assets.LoadAsync<SceneAsset>("levels/forest.scene");
var scenes = world.GetExtension<SceneManager>();
await scenes.LoadSceneAsync(sceneHandle, LoadMode.Additive);
Open Questions
- Scene = World or Scene = Entity subset? Fundamental architecture decision
- Persistent entities - How does the player survive scene transitions?
- Scene dependencies - Scene A requires assets X, Y, Z
- Streaming granularity - Chunks? Rooms? Distance-based?
- Editor integration - How are scenes authored?
- Multi-scene editing - Edit multiple scenes simultaneously?
Recommendation
Start with Scene = Serialized Snapshot approach, building on existing Persistence system. This provides:
- Clear data format (can inspect/edit scene files)
- Additive loading by deserializing into current World
- Entity tagging to track "which scene owns this entity"
- Unloading by despawning tagged entities
Localization
Why It Matters
Any game targeting multiple regions needs:
- Translated text - UI, dialogue, item names
- Formatted values - Numbers, dates, currencies
- Right-to-left support - Arabic, Hebrew
- Asset variants - Different images/audio per language
Current State
- No localization system exists
- UI system (planned) will need localized text
Potential Approaches
Option A: Simple Key-Value
// strings/en.json
{ "menu.start": "Start Game", "menu.quit": "Quit" }
// strings/es.json
{ "menu.start": "Iniciar Juego", "menu.quit": "Salir" }
// Usage
var loc = world.GetExtension<ILocalization>();
string text = loc.Get("menu.start"); // Returns based on current language
Pros: Simple, sufficient for most games Cons: No pluralization, no formatting
Option B: ICU Message Format
// Supports pluralization, gender, etc.
{ "items.count": "{count, plural, =0 {No items} =1 {One item} other {# items}}" }
string text = loc.Format("items.count", new { count = 5 }); // "5 items"
Pros: Industry standard, handles complex cases Cons: More complex syntax, parser needed
Option C: Fluent (Mozilla)
# messages.ftl
hello = Hello, { $name }!
items = { $count ->
[0] No items
[one] One item
*[other] { $count } items
}
Pros: Very expressive, good tooling Cons: Custom syntax, another dependency
Potential Structure
KeenEyes.Localization/
├── Core/
│ ├── ILocalization.cs # Main API
│ ├── Locale.cs # Language + region (en-US, es-MX)
│ ├── LocalizationConfig.cs # Default locale, fallback chain
│ └── LocalizedString.cs # Component-friendly reference
├── Sources/
│ ├── IStringSource.cs # Where strings come from
│ ├── JsonStringSource.cs # Load from JSON files
│ └── CsvStringSource.cs # Load from CSV (translator-friendly)
├── Formatting/
│ ├── IMessageFormatter.cs # Format with parameters
│ └── IcuFormatter.cs # ICU message format support
└── Components/
├── LocalizedText.cs # Component for UI text
└── LocalizedAsset.cs # Component for localized assets
Integration with UI
// UI text component uses localization key
[Component]
public partial struct UIText
{
public string Content; // Direct text OR
public string LocalizationKey; // Key to look up
// ... other fields
}
// UIRenderSystem checks for key
if (!string.IsNullOrEmpty(text.LocalizationKey))
{
var loc = World.GetExtension<ILocalization>();
displayText = loc.Get(text.LocalizationKey);
}
Open Questions
- Format complexity - Simple key-value vs ICU vs Fluent?
- Asset localization - Different textures/audio per language?
- Hot-reload - Change language without restart?
- Fallback chain - en-US → en → default?
- Tooling - How do translators work with the files?
- Font support - CJK characters, Arabic shaping?
Recommendation
Start with simple key-value JSON with parameter substitution. This covers 90% of use cases. Add ICU formatting later if needed. The key architectural decision is the LocalizedString pattern - using keys in components rather than raw text.
Networking
Why It Matters
Multiplayer games need:
- State synchronization - Keep all clients' worlds consistent
- Input handling - Send player actions to server
- Latency compensation - Hide network delay from players
- Authority - Who decides what's "true"?
Current State
- Networking plugin is being discussed separately
- This section captures ECS-specific considerations
ECS Networking Challenges
Challenge 1: Entity Identity
Entities are local IDs (int Id, int Version). How do we reference entities across network?
// Option A: Network ID component
[Component]
public partial struct NetworkIdentity
{
public uint NetworkId; // Globally unique across all clients
public bool IsOwner; // Does this client control this entity?
}
// Option B: Deterministic spawning
// Same spawn order = same entity IDs (requires lockstep)
Challenge 2: Component Replication
Which components sync across network? How often?
// Mark components for replication
[Component]
[Replicated(Frequency = ReplicationFrequency.EveryFrame)]
public partial struct Transform3D { ... }
[Component]
[Replicated(Frequency = ReplicationFrequency.OnChange)]
public partial struct Health { ... }
[Component] // Not replicated - client-only visual
public partial struct ParticleEmitter { ... }
Challenge 3: Authority Model
| Model | Description | Use Case |
|---|---|---|
| Server Authoritative | Server owns all state, clients are dumb | Competitive, anti-cheat |
| Client Authoritative | Clients own their entities | Cooperative, trust players |
| Distributed | Different entities owned by different peers | P2P games |
Challenge 4: Prediction & Rollback
Client predicts locally, server corrects. How to rollback ECS state?
// Snapshot system for rollback
public class NetworkRollbackSystem : SystemBase
{
private readonly RingBuffer<WorldSnapshot> history;
public void Rollback(int serverTick)
{
var snapshot = history.Get(serverTick);
// Restore world state to snapshot
// Re-simulate from serverTick to now
}
}
Potential Structure
KeenEyes.Networking/
├── Core/
│ ├── INetworkTransport.cs # UDP, WebSocket, etc.
│ ├── NetworkManager.cs # Connection management
│ └── NetworkConfig.cs # Tick rate, interpolation settings
├── Identity/
│ ├── NetworkIdentity.cs # Component for networked entities
│ ├── NetworkSpawner.cs # Spawn entities across network
│ └── OwnershipManager.cs # Track who owns what
├── Replication/
│ ├── ReplicatedAttribute.cs # Mark components for sync
│ ├── DeltaCompressor.cs # Only send changes
│ └── ReplicationSystem.cs # Serialize and send state
├── Prediction/
│ ├── InputBuffer.cs # Queue inputs for server
│ ├── PredictionSystem.cs # Client-side prediction
│ └── ReconciliationSystem.cs # Handle server corrections
└── Transport/
├── UdpTransport.cs # Raw UDP
├── LiteNetLibTransport.cs # Reliable UDP library
└── WebSocketTransport.cs # Browser support
Open Questions
- Authority model - Server authoritative default?
- Transport - UDP, WebSocket, or pluggable?
- Tick rate - Fixed tick for determinism?
- Interpolation - How to smooth entity movement?
- Interest management - Only replicate nearby entities?
- Snapshot vs delta - Full state or changes only?
- P2P support - Or server-only architecture?
Recommendation
Networking is complex enough to warrant its own deep-dive planning session. Key decisions:
- Pick an authority model (recommend server-authoritative)
- Define replication attributes via source generator
- Use existing Persistence serialization for snapshots
- Consider wrapping a proven library (LiteNetLib, ENet)
Priority Matrix
| System | Complexity | Impact | Recommendation |
|---|---|---|---|
| Pathfinding | Medium | High (completes AI) | Plan next, implement after AI |
| Scene Management | Medium | Medium (large games only) | Research issue, defer implementation |
| Localization | Low | Medium (release requirement) | Simple implementation early |
| Networking | High | High (if multiplayer) | Separate initiative, deep planning |
Suggested Next Steps
- Create Pathfinding milestone - Pairs with AI, concrete scope
- Create research issue for Scene Management - Explore options, don't implement yet
- Add Localization to UI milestone - Simple key-value system
- Continue Networking discussion - Dedicated planning effort
Summary
These four systems represent the remaining "big picture" items for KeenEyes to become a complete game engine. Pathfinding and Localization are relatively bounded problems. Scene Management and Networking are architectural decisions that affect many other systems.
The existing foundation (ECS, Physics, Persistence, Parallelism) provides a solid base. The planned systems (Graphics, Input, UI, Audio, Particles, Animation, AI, Assets) cover most game needs. These four fill the remaining gaps for shipping real games.