Skip to content

ECS System

Thomaltarix edited this page Nov 3, 2024 · 7 revisions

What's a System?

A system is a piece of logic that processes entities with specific components. Systems operate on entities that have the required components. For example:

  • A MovementSystem might process all entities with both PositionComponent and VelocityComponent to update their positions based on their velocities.
  • A RenderSystem would draw all entities that have a SpriteComponent.

How to Write One

In our game engine, developers and the community can create custom systems and components. Components are written in C++.

Tutorial

If you haven’t already read the ECS Component section, please take a moment to review it.

In this tutorial, we'll create a MovementSystem. This system requires two components: a PositionComponent and a VelocityComponent. The system will update the position based on the velocity.

Let's Implement It

First, create a MovementSystem.hpp file. Include the following files:

#include "VelocityComponent.hpp"
#include "PositionComponent.hpp"
#include "Engine/Shared/Inteface/ISystem.hpp"
  • Velocity and Position: two components (see ECS Component to create a component)(This files already includes IComponent.hpp)
  • ISystem: the interface class for system. To create a new system, it must inherit from this interface.

Now, let's create our system class. It must inherit from the ASystem class.

class MovementSystem : public ASystem {

    public:

        MovementSystem(); // Constructor

        ~MovementSystem() = default; // Default destructor
};

Then, ISystem has a single method to override, it's the method to get the function system:

class MovementSystem : public ASystem {

    public:

        std::function<void(ECS::Registry& reg, int idxPacketEntities)> getFunction() {
        };
};

This method takes the Registry and the IdxPacketEntities as parameter. Like this, we can simply create our system function. The function will get the components Position and Velocity and add the Velocity to the Position of all entities.

MovementSystem::MovementSystem() :
    ASystem("MovementSystem") {}

void _updatePosition(ECS::Registry& entityManager, int idxPacketEntities) {

    // we get all positions and velocities in the registry
    ECS::SparseArray<IComponent> velocities = entityManager.get_components<IComponent>("VelocityComponent");
    ECS::SparseArray<IComponent> positions = entityManager.get_components<IComponent>("PositionComponent");

    // Then for each of positions and velocities...
    for (std::size_t entity = 0; entity < velocities.size() && entity < positions.size(); entity ++) {
        // ... we cast the IComponent into the right type ...
        std::shared_ptr<VelocityComponent> velocity = std::dynamic_pointer_cast<VelocityComponent>(velocities[entity]);
        std::shared_ptr<PositionComponent> position = std::dynamic_pointer_cast<PositionComponent>(positions[entity]);

        // ... if they exist ...
        if (!velocity || !position)
            continue;

        // ... and update the Position of an entity by adding the velocity to the position
        position->x += velocity->dx
        position->y += velocity->dy
    }
};

Finally, in our method getFunction() we want to return our system function:

std::function<void(ECS::Registry& reg)> getFunction()
{
    return [this](ECS::Registry& reg) {
        _updatePosition(reg);
    };
}

Here we are ! We have our first system !

But that's not the end. How will the game Engine use our system ?

For that, we must create an entry point. When the system will load in the game Engine, it will call this entry point to get the System class.

For that, create a cpp file named MovementSystem.cpp put this on it:

#include "MovementSystem.hpp"

extern "C" {
    EXPORT_SYMBOL ISystem* loadSystemInstance() {
        return new MovementSystem();
    }
}

This function will return our System class previously created, the EXPORT_SYMBOL is a macro use to make windows able to open .dll files.

Compile your System with this command in your terminal:

g++ -fPIC -shared -o theNameOfYourSystem.so yourFile.cpp yourComponent1.a yourComponent2.a ...

Don't forget to add all the needed components at the compilation to avoid errors.

Replace theNameOfYourSystem by the name you want to give to your System file, yourFile.cpp by the MovementSystem.cpp and yourComponent1.a, etc... by the right names.

Finally, place your file .so in the Game/Engine/ folder. The system will be automatically loaded when the game Engine will be running.