Entity-Component System (ECS)

The Entity-Component System (ECS) is a design pattern used in game development to represent game objects and their behavior. It is a powerful and flexible approach that allows for easy code reuse and modularity.

Design Overview:

The ECS pattern separates data (components) from logic (systems).

  • Entities: Unique identifiers that represent objects in the game world.
  • Components: Data structures that hold information about an entity, such as position, velocity, health, or animation state.
  • Systems: Logic that operates on entities based on their components.

Implementation Details:

Entity: An entity is simply a unique identifier (an integer ID in this implementation). It has no data associated with it, only the components it references.

Component: Components are data structures that define the properties of an entity. For example, a Position component might contain the entity’s X and Y coordinates.

System: Systems are responsible for processing entities based on their components. For example, a MovementSystem might update the position of entities with a Position and Velocity component.

Code Example:

// From: https://github.com/stevedunn/gleed2d/blob/master/gleed/src/core/gleed_ecs.cpp
          #include "gleed_ecs.h"
          
          int gleed::ECS::createEntity()
          {
              // Increment and return the next entity ID.
              return ++m_NextEntity;
          }
          
          void gleed::ECS::destroyEntity(int entityId)
          {
              // Remove the entity from the list of entities and set its ID to -1 to mark it as invalid.
              if (m_Entities.find(entityId) != m_Entities.end())
              {
                  m_Entities.erase(entityId);
              }
              m_DestroyedEntities.insert(entityId);
          }
          

Using the ECS:

  1. Create an entity: Use createEntity() to create a new entity.
  2. Attach components: Attach components to the entity using attachComponent().
  3. Run systems: Update the game state by running systems that process entities based on their components.

Benefits of ECS:

  • Flexibility: Easily add or remove components from entities, enabling dynamic behavior.
  • Code Reusability: Systems can operate on different entities with the same components, promoting code reuse.
  • Modularity: Code organization is improved by separating data and logic.
  • Performance: Optimized for efficient data processing.

Code Example:

// From: https://github.com/stevedunn/gleed2d/blob/master/gleed/src/core/gleed_ecs.cpp
          void gleed::ECS::attachComponent(int entityId, gleed::Component *component)
          {
              // Attach a component to an entity.
              if (m_Entities.find(entityId) != m_Entities.end())
              {
                  // Add the component to the entity's list of components.
                  m_Entities[entityId].insert(component);
          
                  // Add the component to the global component list.
                  // The component map allows to lookup entities based on a component type.
                  auto it = m_Components.find(typeid(*component));
                  if (it == m_Components.end())
                  {
                      m_Components.insert(std::make_pair(typeid(*component), std::unordered_map<int, gleed::Component*>{}));
                  }
                  m_Components[typeid(*component)][entityId] = component;
              }
          }
          

Example Scenarios:

  • Player Movement:

    • Entity: Player
    • Components: Position, Velocity, Input
    • System: MovementSystem (updates position based on velocity and input).
  • Enemy AI:

    • Entity: Enemy
    • Components: Position, Health, Target
    • System: AISystem (manages enemy behavior based on target and health).
  • Particle Effects:

    • Entity: Particle
    • Components: Position, Velocity, LifeTime, Color
    • System: ParticleSystem (updates particle properties and removes expired particles).

Notes:

  • This ECS implementation uses a hash table to store entities and their components, providing fast access.
  • The Components map allows to efficiently iterate through entities based on a specific component type.
  • The DestroyedEntities set stores IDs of destroyed entities to prevent reusing invalid IDs.

This ECS design pattern provides a flexible and modular approach to game development. The separation of data and logic allows for easy code reuse and makes it easier to manage complex game objects.