Skip to content

Getting Started with Tecs

Jacob Wirth edited this page Mar 24, 2023 · 1 revision

Getting Started with Tecs

This guide will help you get started with Tecs, a C++17 Entity-Component System (ECS) library. We will cover the basics of creating an ECS instance, defining components, working with entities, and using multiple threads.

1. Creating an ECS instance and defining components

To create an ECS instance, first, define your components. For this example, we will use the following component types: Name, Position, and Velocity.

// Define the components
#include <string>
#include "Tecs.hh"

typedef std::string Name;

struct Position {
    float x;
    float y;

    Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
};

struct Velocity {
    float dx;
    float dy;

    Velocity(float dx = 0.0f, float dy = 0.0f) : dx(dx), dy(dy) {}
};

Now, create an ECS instance with the defined components:

// Create the ECS instance
Tecs::ECS<Name, Position, Velocity> ecs;

It may be helpful to create a type alias with all registered components to use instead.

using ECS = Tecs::ECS<Name, Position, Velocity>;
ECS ecs;

2. Creating entities and setting component data

To create a new entity, you need to start a transaction with the AddRemove permission. You can then set, unset, or modify component data for the entity.

// Create a new entity with a name, position, and velocity
{
    auto lock = ecs.StartTransaction<Tecs::AddRemove>();
    Tecs::Entity entity = lock.NewEntity();

    // Set component data
    lock.Set<Name>(entity, "Entity 1");
    lock.Set<Position>(entity, 10.0f, 20.0f);
    lock.Set<Velocity>(entity, 1.0f, 0.5f);
}

// Remove a component or entity
{
    auto lock = ecs.StartTransaction<Tecs::AddRemove>();
    Tecs::Entity entity; // Assume this is a valid entity
    lock.Unset<Name>(entity);
    entity.Destroy(lock); // Will deconstruct all remaining components
}

3. Looping over entities based on their components

To loop over entities with specific component types, you can use the EntitiesWith function. This function returns an EntityView object, which is an iterable collection of entities with the specified components.

// Loop over all entities with a Position and Velocity component
{
    auto lock = ecs.StartTransaction<Tecs::Read<Position, Velocity>>();

    for (const Tecs::Entity &entity : lock.EntitiesWith<Position>()) {
        if (!entity.Has<Position, Velocity>(lock)) continue;
        const Position &pos = lock.Get<Position>(entity);
        const Velocity &vel = lock.Get<Velocity>(entity);
        // Process the entity...
    }
}

4. A full working example using 2 threads

This example demonstrates basic usage of multi-threading with Tecs. It creates two threads, one for updating entity positions based on their velocity, and another for printing entity names and their positions to the log.

#include <iostream>
#include <thread>
#include <chrono>
#include "Tecs.hh"

// ... Define Name, Position, and Velocity components

using ECS = Tecs::ECS<Name, Position, Velocity>;

// Function to update entity positions based on their velocity
void UpdatePositions(ECS &ecs) {
    while (true) {
        auto lock = ecs.StartTransaction<Tecs::Write<Position, Velocity>>();

        for (const Tecs::Entity &entity : lock.EntitiesWith<Position>()) {
            if (!entity.Has<Position, Velocity>(lock)) continue;
            Position &pos = lock.Get<Position>(entity);
            const Velocity &vel = lock.Get<Velocity>(entity);

            pos.x += vel.dx;
            pos.y += vel.dy;
        }
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

// Function to print entity names and their positions
void PrintEntities(ECS &ecs) {
    while (true) {
        auto lock = ecs.StartTransaction<Tecs::Read<Name, Position>>();

        for (const Tecs::Entity &entity : lock.EntitiesWith<Name>()) {
            if (!entity.Has<Name, Position>(lock)) continue;
            const Name &name = lock.Get<Name>(entity);
            const Position &pos = lock.Get<Position>(entity);

            std::cout << name << ": (" << pos.x << ", " << pos.y << ")" << std::endl;
        }
    }

    std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main() {
    ECS ecs;

    // ... Create entities and set component data

    std::thread updateThread(UpdatePositions, std::ref(ecs));
    std::thread printThread(PrintEntities, std::ref(ecs));
    updateThread.join();
    printThread.join();
    return 0;
}