Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/raoulkdev/MiniECS/llms.txt

Use this file to discover all available pages before exploring further.

A MiniECS::Unit is a named entity that lives inside a World. By itself a Unit carries no behaviour — it is purely a container that groups one or more Modules together under a shared identity. Behaviour enters the picture only when Modules are attached: at each game loop tick the World asks every Unit to call start() on all of its Modules. Think of a Unit the same way you would think of a Unity GameObject: an addressable object in the scene whose capabilities are defined entirely by its components.

Creating Units

Units are always created through World::createUnit(). This method constructs the Unit internally and transfers ownership to the World’s std::unique_ptr vector. You never instantiate Unit directly.
MiniECS::World scene("Scene");

// Creates a Unit named "u1" owned by the World
scene.createUnit("u1");
Once created, you reach the Unit through the World’s accessor methods:
// By name
scene.getUnitByName("u1")->addModule(MiniECS::ModuleType::Transform);

// By vector index
scene.getUnitByIndex(0)->addModule(MiniECS::ModuleType::Renderer);

Adding Modules

addModule(ModuleType moduleType) is the primary way to attach behaviour to a Unit.
unit->addModule(MiniECS::ModuleType::Renderer);
unit->addModule(MiniECS::ModuleType::Transform);
Internally, addModule first scans the existing Module list for a duplicate ModuleType. If a match is found the insertion is aborted and a message is printed to stdout — no exception is thrown. This guarantees that a Unit holds at most one Module of each type. If an unrecognised ModuleType is passed, the default case prints "Invalid Module Type!!" to stdout.
void MiniECS::Unit::addModule(ModuleType moduleType)
{
    bool duplicate = false;
    for (std::unique_ptr<Module>& module : modules)
    {
        if (module->getType() == moduleType)
        {
            duplicate = true;
            std::cout << "Module type already exsists, cannot add it!\n";
        }
    }
    if (!duplicate)
    {
        switch (moduleType)
        {
            case ModuleType::Renderer:
                modules.push_back(std::make_unique<RendererModule>(ModuleType::Renderer));
                break;
            case ModuleType::Transform:
                modules.push_back(std::make_unique<TransformModule>(ModuleType::Transform));
                break;
            default:
                std::cout << "Invalid Module Type!! \n";
                break;
        }
    }
}
The switch statement maps each ModuleType enum value to its concrete derived class:
ModuleTypeConcrete class
ModuleType::RendererMiniECS::RendererModule
ModuleType::TransformMiniECS::TransformModule
When you register a custom Module type, you add a new case to this switch and a corresponding entry to the ModuleType enum. The duplicate-check logic requires no changes.

Removing Modules

removeModule(ModuleType moduleType) erases the Module whose getType() matches the argument. If no matching Module is found, a message is printed to stdout.
unit->removeModule(MiniECS::ModuleType::Renderer);

Retrieving Modules

getModule<T>() is a templated accessor that returns a raw pointer to the first Module in the vector that can be dynamic_cast to T. If no such Module exists it writes an error to std::cerr and returns nullptr.
// Retrieve the TransformModule and set the initial position
auto* transform = scene.getUnitByName("u1")->getModule<MiniECS::TransformModule>();
if (transform)
{
    transform->position = {22.5f, 33.4f};
}
The template implementation lives in Unit.h:
template<typename T>
T* getModule()
{
    for (auto& module : modules)
    {
        if (auto* result = dynamic_cast<T*>(module.get()))
        {
            return result;
        }
    }
    std::cerr << "Module of requested type not found in unit: " << name << std::endl;
    return nullptr;
}
Always check the return value of getModule<T>() for nullptr before dereferencing. The method returns nullptr when the requested Module has not been added to the Unit.

Other Methods

getName()

Returns the Unit’s name string as set at construction time.
std::string unitName = unit->getName(); // e.g. "u1"

getModulesVector()

Returns a std::vector<std::unique_ptr<Module>>& — a mutable reference to the internal Module list. Primarily used by World::play() to check whether the Unit has any Modules before entering the game loop.
auto& mods = unit->getModulesVector();
std::cout << "Module count: " << mods.size() << "\n";

callModuleStart()

Called by World::play() once per frame. Iterates over every Module in the vector and invokes start() on each one. You generally do not call this directly.
void MiniECS::Unit::callModuleStart() const
{
    for (const std::unique_ptr<Module>& sModule : modules)
    {
        sModule->start();
    }
}

Complete Example

#include "modules/transform/TransformModule.h"
#include "unit/Unit.h"
#include "world/World.h"

int main()
{
    MiniECS::World scene("Scene");

    // Create the entity
    scene.createUnit("u1");

    // Attach modules
    scene.getUnitByName("u1")->addModule(MiniECS::ModuleType::Renderer);
    scene.getUnitByName("u1")->addModule(MiniECS::ModuleType::Transform);

    // Attempting to add Transform a second time is silently rejected
    scene.getUnitByName("u1")->addModule(MiniECS::ModuleType::Transform);

    // Retrieve a typed pointer and set state
    scene.getUnitByName("u1")->getModule<MiniECS::TransformModule>()->position = {22.5f, 33.4f};

    // Start the simulation
    scene.play();
}

Build docs developers (and LLMs) love