Table of Contents

API Reference

Welcome to the KeenEyes ECS API documentation.

This reference is automatically generated from XML documentation comments in the source code. For tutorials and guides, see the Documentation.

Namespaces

KeenEyes

Core ECS runtime types for building high-performance entity component systems.

Type Description
World Main ECS container - manages entities, components, systems, and queries
@KeenEyes.Entity Lightweight entity handle (Id, Version) for staleness detection
EntityBuilder Fluent builder for spawning entities with components
@KeenEyes.IComponent Marker interface for component structs
@KeenEyes.ITagComponent Marker interface for zero-size tag components
ComponentRegistry Per-world component type registration
Archetype Storage for entities sharing the same component types
ArchetypeManager Manages archetype lifecycle and entity migrations
QueryBuilder Fluent query builder for filtering entities
QueryEnumerator Cache-friendly query iteration
@KeenEyes.ISystem Base interface for ECS systems
@KeenEyes.SystemBase Abstract base class with lifecycle hooks and runtime control
@KeenEyes.SystemGroup Groups systems for collective execution
@KeenEyes.SystemPhase Enum defining execution phases (EarlyUpdate, FixedUpdate, Update, LateUpdate, Render, PostRender)
@KeenEyes.RunBeforeAttribute Specifies this system must run before another system (topologically sorted)
@KeenEyes.RunAfterAttribute Specifies this system must run after another system (topologically sorted)
EntityPool Manages entity ID recycling with versioning
@KeenEyes.MemoryStats Memory usage statistics snapshot

KeenEyes.Abstractions

Lightweight interfaces and types for plugin development without Core dependency.

Type Description
@KeenEyes.IWorld Plugin-facing interface for world operations
@KeenEyes.IEntityBuilder Interface for fluent entity building
@KeenEyes.ICommandBuffer Interface for deferred entity operations
@KeenEyes.CommandBuffer Queues entity operations for deferred execution (20-50x faster than reflection)
@KeenEyes.EntityCommands Fluent builder for queued entity spawns
@KeenEyes.IWorldPlugin Base interface for modular world extensions
@KeenEyes.IPluginContext Context for plugin installation with system registration
@KeenEyes.EventSubscription Disposable subscription handle for event cleanup

KeenEyes.Events

Types for reactive programming patterns - events, subscriptions, and change tracking.

Type Description
EventBus Generic pub/sub event system for custom events
@KeenEyes.Events.EventSubscription Disposable subscription handle for event cleanup

KeenEyes.Generators.Attributes

Attributes for Roslyn source generators that reduce boilerplate code.

Attribute Description
@KeenEyes.ComponentAttribute Generates WithComponentName() fluent builder methods
@KeenEyes.TagComponentAttribute Generates parameterless tag methods for marker components
@KeenEyes.DefaultValueAttribute Specifies default values for component fields in builders
@KeenEyes.BuilderIgnoreAttribute Excludes a field from generated builder parameters
@KeenEyes.QueryAttribute Generates efficient query iterator structs
@KeenEyes.SystemAttribute Generates system metadata (Phase, Order, Group)

Getting Started

The main entry point is the World class:

using KeenEyes;

// Create an isolated ECS world
using var world = new World();

// Create an entity with components using the fluent builder
var entity = world.Spawn()
    .With(new Position { X = 0, Y = 0 })
    .With(new Velocity { X = 1, Y = 0 })
    .Build();

// Query and iterate over matching entities
foreach (var e in world.Query<Position, Velocity>())
{
    ref var pos = ref world.Get<Position>(e);
    ref readonly var vel = ref world.Get<Velocity>(e);

    pos.X += vel.X;
    pos.Y += vel.Y;
}

Key Concepts

Concept Description
World Isolated container for entities, components, and systems. Each world has its own component registry - no static state.
Entity Lightweight (Id, Version) tuple. Version increments on recycle for staleness detection.
Component Plain data struct implementing IComponent. Use ITagComponent for zero-size markers.
Archetype Internal storage grouping entities with identical component types for cache-friendly iteration.
Query Fluent API for filtering entities: world.Query<A, B>().With<C>().Without<D>()
System Logic that processes entities. Inherit from SystemBase for lifecycle hooks (OnBeforeUpdate, OnAfterUpdate, OnEnabled, OnDisabled) and runtime control.
SystemPhase Execution stage: EarlyUpdate → FixedUpdate → Update → LateUpdate → Render → PostRender.
CommandBuffer Queues spawn/despawn/component operations for safe execution outside iteration.
Singleton World-level resources via SetSingleton<T>() / GetSingleton<T>().
Hierarchy Parent-child entity relationships via SetParent() / GetChildren().
Messaging Inter-system communication via Send<T>() / Subscribe<T>() with immediate or queued delivery.
Plugin Modular extensions via IWorldPlugin with Install() / Uninstall() lifecycle and Extension API.
Events Lifecycle events via OnEntityCreated(), OnComponentAdded<T>(), etc.
Change Tracking Dirty entity tracking via MarkDirty<T>() / GetDirtyEntities<T>().
Prefabs Reusable entity templates via RegisterPrefab() / SpawnFromPrefab() with inheritance support.
String Tags Runtime-flexible tagging via AddTag() / HasTag() / QueryByTag() for data-driven workflows.
Component Validation Enforce component dependencies and conflicts via [RequiresComponent] / [ConflictsWith] attributes.

Quick Reference

Entity Operations

// Spawn
var entity = world.Spawn()
    .With(new Position { X = 0, Y = 0 })
    .WithTag<Player>()
    .Build();

// Check and get components
if (world.Has<Position>(entity))
{
    ref var pos = ref world.Get<Position>(entity);
    pos.X += 10;
}

// Add/remove components
world.Add(entity, new Velocity { X = 1, Y = 0 });
world.Remove<Velocity>(entity);

// Despawn
world.Despawn(entity);

Queries

// Basic query - entities with Position AND Velocity
foreach (var e in world.Query<Position, Velocity>())
{
    ref var pos = ref world.Get<Position>(e);
    ref readonly var vel = ref world.Get<Velocity>(e);
}

// Filtered query - with Health, without Dead tag
foreach (var e in world.Query<Position>().With<Health>().Without<Dead>())
{
    // Process living entities with position and health
}

Systems

[System(Phase = SystemPhase.Update, Order = 10)]
public partial class MovementSystem : SystemBase
{
    protected override void OnBeforeUpdate(float deltaTime)
    {
        // Called before Update - setup, accumulation, etc.
    }

    public override void Update(float deltaTime)
    {
        foreach (var entity in World.Query<Position, Velocity>())
        {
            ref var pos = ref World.Get<Position>(entity);
            ref readonly var vel = ref World.Get<Velocity>(entity);
            pos.X += vel.X * deltaTime;
            pos.Y += vel.Y * deltaTime;
        }
    }

    protected override void OnAfterUpdate(float deltaTime)
    {
        // Called after Update - cleanup, statistics, etc.
    }

    protected override void OnEnabled()
    {
        // Called when system is enabled
    }

    protected override void OnDisabled()
    {
        // Called when system is disabled
    }
}

// Register with phase and order
world.AddSystem<InputSystem>(SystemPhase.EarlyUpdate, order: 0);
world.AddSystem<MovementSystem>(SystemPhase.Update, order: 10);
world.AddSystem<RenderSystem>(SystemPhase.Render, order: 0);

// System dependencies with [RunBefore] and [RunAfter]
[System(Phase = SystemPhase.Update)]
[RunAfter(typeof(InputSystem))]
[RunBefore(typeof(RenderSystem))]
public partial class AnimationSystem : SystemBase { }

// Or specify at registration time
world.AddSystem<CollisionSystem>(
    SystemPhase.Update,
    order: 0,
    runsBefore: [typeof(DamageSystem)],
    runsAfter: [typeof(MovementSystem)]);

// Run all systems (sorted by phase, then dependencies, then order)
world.Update(deltaTime);

// Run only FixedUpdate phase systems (for physics)
world.FixedUpdate(fixedDeltaTime);

// Runtime control
var system = world.GetSystem<MovementSystem>();
world.DisableSystem<MovementSystem>();  // Pauses the system
world.EnableSystem<MovementSystem>();   // Resumes the system

Command Buffer (Deferred Operations)

CommandBuffer is now in KeenEyes.Abstractions for plugin isolation, with zero reflection for 20-50x performance improvement:

using KeenEyes; // From Abstractions - no Core dependency needed!

var buffer = new CommandBuffer();

foreach (var entity in world.Query<Health>())
{
    ref var health = ref world.Get<Health>(entity);
    if (health.Current <= 0)
    {
        // Queue for later - safe during iteration
        buffer.Despawn(entity);
    }
}

// Execute all queued commands (delegate-based, zero reflection)
buffer.Flush(world);

Performance: Commands use delegate capture pattern instead of reflection for 20-50x faster execution.

Singletons

// Set world-level resource
world.SetSingleton(new GameConfig { Gravity = 9.8f });

// Retrieve
ref var config = ref world.GetSingleton<GameConfig>();

Entity Hierarchy

// Establish parent-child relationship
world.SetParent(child, parent);

// Query relationships
var parent = world.GetParent(child);
foreach (var child in world.GetChildren(parent))
{
    // Process children
}

// Destroy entity and all descendants
world.DespawnRecursive(rootEntity);

Messaging (Inter-System Communication)

// Define message types (struct recommended for zero-allocation)
public readonly record struct DamageMessage(Entity Target, int Amount, Entity Source);

// Subscribe to messages
var subscription = world.Subscribe<DamageMessage>(msg =>
{
    ref var health = ref world.Get<Health>(msg.Target);
    health.Current -= msg.Amount;
});

// Send immediately to all subscribers
world.Send(new DamageMessage(target, 25, attacker));

// Or queue for deferred processing
world.QueueMessage(new DamageMessage(target, 25, attacker));
world.ProcessQueuedMessages();  // Process all queued messages

// Check if anyone is listening (optimization)
if (world.HasMessageSubscribers<ExpensiveMessage>())
{
    var data = ComputeExpensiveData();
    world.Send(new ExpensiveMessage(data));
}

// Unsubscribe
subscription.Dispose();

Events

// Component lifecycle events
var sub1 = world.OnComponentAdded<Health>((entity, health) =>
{
    Console.WriteLine($"Health added to {entity}");
});

// Entity lifecycle events
var sub2 = world.OnEntityDestroyed(entity =>
{
    Console.WriteLine($"Entity {entity} destroyed");
});

// Custom events via EventBus
world.Events.Subscribe<DamageEvent>(evt =>
{
    Console.WriteLine($"Damage dealt: {evt.Amount}");
});
world.Events.Publish(new DamageEvent(target, 25));

// Cleanup subscriptions
sub1.Dispose();
sub2.Dispose();

Change Tracking

// Mark entity as dirty
world.MarkDirty<Position>(entity);

// Query dirty entities
foreach (var e in world.GetDirtyEntities<Position>())
{
    SyncToNetwork(e);
}

// Clear after processing
world.ClearDirtyFlags<Position>();

// Enable automatic tracking for Set() calls
world.EnableAutoTracking<Position>();

Plugins

// Create plugin
public class PhysicsPlugin : IWorldPlugin
{
    public string Name => "Physics";

    public void Install(PluginContext context)
    {
        context.AddSystem<GravitySystem>(SystemPhase.FixedUpdate, order: 0);
        context.SetExtension(new PhysicsWorld(context.World));
    }

    public void Uninstall(PluginContext context)
    {
        context.RemoveExtension<PhysicsWorld>();
    }
}

// Install via WorldBuilder
using var world = new WorldBuilder()
    .WithPlugin<PhysicsPlugin>()
    .Build();

// Or install directly
world.InstallPlugin<PhysicsPlugin>();

// Query plugins
if (world.HasPlugin<PhysicsPlugin>())
{
    var plugin = world.GetPlugin<PhysicsPlugin>();
}

// Access extensions
var physics = world.GetExtension<PhysicsWorld>();
physics.SetGravity(9.8f);

// Uninstall
world.UninstallPlugin<PhysicsPlugin>();

Prefabs

// Define reusable entity templates
var enemyPrefab = new EntityPrefab()
    .With(new Position { X = 0, Y = 0 })
    .With(new Health { Current = 100, Max = 100 })
    .WithTag<EnemyTag>();

world.RegisterPrefab("Enemy", enemyPrefab);

// Spawn from prefab
var enemy = world.SpawnFromPrefab("Enemy").Build();

// Override components when spawning
var strongEnemy = world.SpawnFromPrefab("Enemy")
    .With(new Health { Current = 200, Max = 200 })
    .Build();

// Prefab inheritance
var bossEnemyPrefab = new EntityPrefab()
    .Extends("Enemy")
    .With(new Health { Current = 500, Max = 500 })
    .WithTag<BossTag>();

world.RegisterPrefab("BossEnemy", bossEnemyPrefab);

String Tags

// Add runtime tags
world.AddTag(entity, "Enemy");
world.AddTag(entity, "Hostile");

// Check tags
if (world.HasTag(entity, "Boss"))
{
    // Special boss handling
}

// Query by tag
foreach (var e in world.QueryByTag("Enemy"))
{
    // Process all enemies
}

// Combine with component queries
foreach (var e in world.Query<Position>()
    .WithTag("Enemy")
    .WithoutTag("Dead"))
{
    // Process living enemies with position
}

Component Validation

// Enforce component dependencies with attributes
[Component]
[RequiresComponent(typeof(Transform))]
public partial struct RigidBody
{
    public float Mass;
}

// Mutual exclusion
[Component]
[ConflictsWith(typeof(DynamicBody))]
public partial struct StaticBody { }

// This throws - Transform is required
var entity = world.Spawn()
    .With(new RigidBody { Mass = 1.0f })
    .Build();

// This works
var entity = world.Spawn()
    .With(new Transform())
    .With(new RigidBody { Mass = 1.0f })
    .Build();

// Custom validators
world.RegisterValidator<Health>((world, entity, health) =>
    health.Current >= 0 && health.Current <= health.Max);

// Control validation mode
world.ValidationMode = ValidationMode.DebugOnly;

Source Generator Attributes

Reduce boilerplate with source-generated code:

using KeenEyes.Generators.Attributes;

// Generates WithPosition(float x, float y) builder method
[Component]
public partial struct Position
{
    public float X;
    public float Y;
}

// Generates WithPlayer() parameterless method
[TagComponent]
public partial struct Player { }

// Use generated methods
var entity = world.Spawn()
    .WithPosition(x: 10, y: 20)
    .WithPlayer()
    .Build();

Guides

For in-depth coverage, see the documentation guides:

Browse the namespace documentation below for detailed API information on each type.