-
Notifications
You must be signed in to change notification settings - Fork 1
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.
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;
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
}
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...
}
}
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;
}