Creating a Base System Class in an ECS Architecture

The final piece of the puzzle is creating systems. These systems will query for entities with specific components and implement the game logic. For example, a MovementSystem will look for entities with a MovementComponent and update their positions each frame.

Let's establish a foundation for all our future game systems. We'll create a base System class that other systems can inherit from. This goes a long way towards ECS goal of having a common interface for all code.

// System.ts
import { Entity, EntityManager } from "./Entity";
import { Component } from "./Component";
import { Scene } from "@babylonjs/core";

export abstract class System {
  protected entities: Entity[] = [];
  protected componentClasses: Component[];
  protected scene: Scene;
  protected entityManager: EntityManager;

  constructor(
    entityManager: EntityManager,
    componentClasses: ComponentClass[],
  ) {
    this.entityManager = entityManager;
    if (!entityManager.entities) {
      console.error("EntityManager has no entities");
      return;
    }
    const entities = entityManager.entities.values();
    this.scene = entityManager.scene;
    this.componentClasses = componentClasses;
    for (const entity of entities) {
      const gotEmAll = this.componentClasses.every((componentClass) => {
        return entity.hasComponent(componentClass);
      });
      if (gotEmAll) this.entities.push(entity);
    }
  }

  // Updates each entity this system is concerned with
  update(deltaTime: number): void {
    this.entities.forEach((entity: Entity) => {
      this.processEntity(entity, deltaTime);
    });
  }

  // Abstract method to process each entity. This should be implemented by subclasses.
  protected abstract processEntity(entity: Entity, deltaTime: number): void;
}

Explanation:

  • This System class acts as a blueprint for all our game systems. It holds references to the EntityManager, the Scene, and the specific Component classes it requires to function.
  • The update method iterates through all relevant entities and calls the processEntity method for each one.
  • The processEntity method is abstract, meaning it must be implemented by concrete system classes that inherit from System.

In Sum

We've laid the groundwork for our ECS architecture. We have entities, components, and systems. The next step is to create concrete systems that implement the game logic. We'll start with a MovementSystem that updates the position of entities with a MovementComponent.