Skip to content

danjdewhurst/ecs-ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🎮 ECS Game Engine

High-Performance Entity Component System for TypeScript

CI Status Release npm version License: MIT TypeScript Bun Ask DeepWiki

Quick StartFeaturesDocumentationExamplesPhilosophyRoadmap

✨ Features

🚀 Core Performance

  • Archetype-based storage for O(1) queries
  • Entity ID recycling with zero allocations
  • Cache-friendly data structures
  • Dirty tracking for selective updates

🎯 Developer Experience

  • Full TypeScript with strict typing
  • Zero runtime dependencies
  • Hot reloading with Bun
  • Interactive scaffolding for rapid development
  • Comprehensive test coverage

🧩 Architecture

  • System dependencies & scheduling
  • Event-driven communication
  • Plugin system with hot loading
  • Object pooling for memory efficiency

🌐 Multiplayer

  • WebSocket server built-in
  • Type-safe network protocol
  • Session management
  • Real-time synchronization

📜 Core Principles

Entities = IDs • Components = Data • Systems = Logic

Built on proven ECS patterns for maximum performance and maintainability. Learn more →

🚀 Quick Start

Installation

bun add @danjdewhurst/ecs-ts

Requirements: Bun 1.2+

💻 Basic Usage

import { World, BaseSystem, type Component } from "@danjdewhurst/ecs-ts";

// 1. Define components (pure data)
interface Position extends Component {
  readonly type: "position";
  x: number;
  y: number;
}

// 2. Create systems (game logic)
class MovementSystem extends BaseSystem {
  readonly name = "MovementSystem";
  readonly priority = 1;

  update(world: World, deltaTime: number): void {
    const entities = this.queryEntities(world, "position", "velocity");

    for (const entityId of entities) {
      const pos = world.getComponent<Position>(entityId, "position");
      const vel = world.getComponent<Velocity>(entityId, "velocity");

      if (pos && vel) {
        pos.x += vel.dx * deltaTime;
        pos.y += vel.dy * deltaTime;
      }
    }
  }
}

// 3. Create world and run
const world = new World();
world.addSystem(new MovementSystem());

const entity = world.createEntity();
world.addComponent(entity, { type: "position", x: 0, y: 0 });
world.addComponent(entity, { type: "velocity", dx: 10, dy: 5 });

world.update(1 / 60); // Update at 60 FPS

📚 Documentation

Core Concepts

Concept Description Example
World Container for all ECS data const world = new World()
Entity Unique ID representing a game object world.createEntity()
Component Pure data attached to entities { type: 'health', hp: 100 }
System Logic that processes entities class MovementSystem extends BaseSystem
Query Find entities by components world.query('position', 'velocity')

Advanced Features

📡 Event System - Decoupled communication
// Subscribe to events
world.subscribeToEvent("player-death", (event) => {
  console.log(`Player ${event.data.playerId} died`);
});

// Emit events
world.emitEvent({
  type: "player-death",
  timestamp: Date.now(),
  data: { playerId: entity },
});
🔗 System Dependencies - Automatic execution ordering
class PhysicsSystem extends BaseSystem {
  readonly name = "PhysicsSystem";
  readonly priority = 1;
  // No dependencies - runs first
}

class CollisionSystem extends BaseSystem {
  readonly name = "CollisionSystem";
  readonly priority = 10; // Higher priority, but runs AFTER physics
  readonly dependencies = ["PhysicsSystem"]; // Explicit dependency
}

class DamageSystem extends BaseSystem {
  readonly name = "DamageSystem";
  readonly priority = 1;
  readonly dependencies = ["CollisionSystem"]; // Runs after collision
}

// Systems execute in dependency order: Physics → Collision → Damage
// Regardless of add order or priority

// Validate dependencies before adding
const validation = world.validateSystemDependencies([
  new PhysicsSystem(),
  new CollisionSystem(),
]);

// Get execution order
const order = world.getSystemExecutionOrder();
// [PhysicsSystem, CollisionSystem, DamageSystem]

// View dependency graph
const graph = world.getSystemDependencyGraph();

Benefits:

  • ✅ Prevents logic errors from incorrect system ordering
  • ✅ Self-documenting system relationships
  • ✅ Detects circular dependencies with helpful errors
  • ✅ Foundation for future parallel execution
🌐 Multiplayer - WebSocket server built-in
import { GameServer } from "@danjdewhurst/ecs-ts/websocket";

const server = new GameServer(world, {
  port: 3000,
  maxClients: 100,
});

await server.start();
🔌 Plugin System - Extend with custom functionality
class MyPlugin implements Plugin {
  readonly name = "MyPlugin";
  readonly version = "1.0.0";

  async initialize(world: World): Promise<void> {
    // Setup systems, components, etc.
  }
}

const pluginManager = new PluginManager();
await pluginManager.loadPlugin(new MyPlugin());
await pluginManager.initializeAll(world);
🛠️ CLI Scaffolding - Interactive code generation
# Launch interactive scaffolding wizard
bun run scaffold

# OR use direct commands with aliases
bun run scaffold component    # Generate component (alias: c, comp)
bun run scaffold system       # Generate system (alias: s, sys)
bun run scaffold example      # Generate example (alias: e, ex)
bun run scaffold game         # Generate game template (alias: g)
bun run scaffold plugin       # Generate plugin (alias: p, plug)
bun run scaffold --help       # Show all commands and options

# Automatically creates tests and updates index files
# Follows ECS patterns and project conventions

Generate:

  • Components with custom properties and factory functions
  • Systems with dependencies and component queries
  • Examples demonstrating specific functionality
  • Game templates with complete setups
  • Plugins following plugin architecture
⚡ Performance Tools - Optimization utilities
// Object pooling
const bulletPool = new ObjectPool(
  () => ({ x: 0, y: 0, active: false }),
  (bullet) => {
    bullet.active = false;
  }
);

// Dirty tracking for selective updates
world.dirtyTracker.markDirty(entityId, "position");

🧩 Examples

# Clone and run examples
git clone https://github.com/danjdewhurst/ecs-ts.git
cd ecs-ts && bun install

bun examples/basic-example.ts                  # Core ECS
bun examples/system-dependencies-example.ts    # System dependencies
bun examples/event-system-example.ts           # Events
bun examples/websocket-example.ts              # Multiplayer
bun examples/plugin-system-example.ts          # Plugins
bun examples/performance-optimization.ts       # Optimization

🧩 Development

# Install dependencies
bun install

# Development commands
bun test           # Run tests (100% coverage)
bun run typecheck  # Type checking
bun run check      # Lint & format
bun run build      # Build for production
bun run scaffold   # Interactive code scaffolding

# Commit with conventional commits
bun run commit     # Interactive commit helper

Test Coverage

  • ✅ 24 test suites with comprehensive coverage
  • ✅ Core ECS, Events, WebSocket, Plugins, Performance
  • ✅ Unit and integration tests
  • ✅ 100% critical path coverage

📈 Performance

Operation Complexity Notes
Entity Creation O(1) ID recycling
Component Add/Remove O(1) Hash map
Single Query O(1) Archetype lookup
Multi Query O(k) k = matching entities
System Update O(n) n = active entities

🤝 Contributing

Contributions welcome! Please follow conventional commits and ensure all tests pass.

See CONTRIBUTING.md for details.

📄 License

MIT © 2025 danjdewhurst


GitHubnpmIssuesPhilosophyRoadmap

About

A high-performance ECS (Entity Component System) game engine built with TypeScript and Bun

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors