Brenta Engine 1.0
Loading...
Searching...
No Matches
ECS

Brenta Engine features an Entity Component System architecture. The ECS is a design pattern that allows you to structure your code in a way that is more modular and scalable.

The ECS is composed of three main parts:

  • Entity: an entity is an object in the game, it is just an ID
  • Component: a component is a piece of data that is attached to an entity
  • System: a system is a piece of code that operates on entities with specific components. They get autocamically executed at each game tick.
  • Resources: resources are shared global data that can be accessed by any system

With those building blocks, you can create any game or application. The ECS is optional, you can use the engine without it.

Header

Everything you need to use the ECS is in the ecs directory. To use the ECS, include the ecs.hpp header:

#include "ecs.hpp"
using namespace Brenta::ECS;

The World

Everything that exists in the ECS is contained in the World class. The World is a singleton accessible from anywhere in the code and It will be your entry point to the ECS. You can create entities, assign components to them, and query entities with specific components. Let's take a look on how to use the World API.

Creating new Entities

Creating an entity is very straightforward, you can ask the world to create one for you:

Entity entity = World::NewEntity();

This will return a new entity with a unique ID or -1 if the entity could not be created.

Assigning Components to Entities

To assign a component to an entity, first you need to create a component. Components are just structs with data that extend the Component class. Note that you need to implement a default empty constructor for your component.

For example:

struct PositionComponent : public Component {
float x, y, z;
PositionComponent(); // Required
PositionComponent(float x, float y, float z); // Optional
}
Component class.
Definition component.hpp:61

You can find many more examples in examples/components.cpp.

Once you have created a component, you can assign it to an entity:

PositionComponent position(0.0f, 0.0f, 0.0f);
World::AddComponent<PositionComponent>(entity, position);

Creating Systems

Systems are classes that extend the System class. Systems can query entities with specific components and operate on them. They have a run method that will be called at each tick. Systems can define which components to query by specifying them in the template arguments (see the example below), they will receive a vector of entities that match the query.

Note that if your System does not need to query any component, you need to pass None as the template argument.

You can create a system like so:

struct RendererSystem : System<ModelComponent, TransformComponent> {
// you need to implement the run method
void run(std::vector<Entity> matches) const override {
if (matches.empty()) return;
for (auto match : matches) {
// get the components associated to an entity
auto model_component =
World::EntityToComponent<ModelComponent>(match);
// ...
myModel.Draw(default_shader);
}
}
};
System type.
Definition system.hpp:60

Note how we used World::EntityToComponent to get the components associated with an entity. This is a common pattern you will use in your systems.

After you defined your system, you need to register it. This can be done throught a macro:

REGISTER_SYSTEMS(RendererSystem);

Note: You can register as many systems as you want but passing the systems as a comma separated list to the macro, but you have to use REGISTER_SYSTEM once and only once in your code, this is because systems are registered in a global type.

Resources

Resources are data, like components, but they are not associated with entities. Things like the day in the game, the number of enemies, etc should be resources.

You can create a resource like so:

struct DayResource : public Resource {
int day;
DayResource() : day(0) {}
DayResource(int day) : day(day) {}
};
Resource type.
Definition resource.hpp:57

You can register a resource like so:

World::AddResource<WireframeResource>(WireframeResource(false));