Entity-Component-System

Quick Tutorial

Lets create a simple physics engine, which moves the entities on a two-dimensional plane. To move the entities, they need a position and a velocity. So create two different Components: A PositionComponent and a VelocityComponent. To see which entity is at which position, the entities get an additional component containing their name. To use the Entity-Component-System first download the library from the section Source. This is a header-only library, you just need to include the headers in your code correctly, no additional linking is required. Ensure that you enable the c++0x standard [-std=c++0x] for your compiler. If your compiler doesn't support the c++0x standard, manually comment out all static_assert(...) statements in the code.
Now create three files PositionComponent.h, VelocityComponent.h and NameComponent.h as follows:

In PositionComponent.h insert:
#ifndef POSITIONCOMPONENT_H
#define POSITIONCOMPONENT_H

#include "Component.h"

class PositionComponent : public Component<PositionComponent>
{
  public:
PositionComponent(int posX, int posY) : x(posX), y(posY) {}
int x, y;
};

#endif

In VelocityComponent.h insert:
#ifndef VELOCITYCOMPONENT_H
#define VELOCITYCOMPONENT_H

#include "Component.h"

class VelocityComponent : public Component<VelocityComponent>
{
  public:
VelocityComponent(int velX, int velY) : x(velX), y(velY) {}
int x, y;
};

#endif
In NameComponent.h insert:
#ifndef NAMECOMPONENT_H
#define NAMECOMPONENT_H

#include "Component.h"
#include <string>

class NameComponent : public Component<NameComponent>
{
  public:
NameComponent(std::string newName) : name(newName) {}
std::string name;
};

#endif

Now all data needed to process the entities is available. The code to manipulate the entities goes in a system. Here we need a system which updates the position and a system that displays the position of the entities. Create four files and call them MovementSystem.h, MovementSystem.cpp, RenderSystem.h and RenderSystem.cpp and modify them as follows:

In MovementSystem.h insert:
#ifndef MOVEMENTSYSTEM_H
#define MOVEMENTSYSTEM_H

#include "System.h"

class MovementSystem : public System<MovementSystem>
{
  public:
MovementSystem();
void setTimeDelta(int time);
  protected:
void update(Entity e);
  private:
int dt;
};

#endif

In MovementSystem.cpp insert:
#include "MovementSystem.h"
#include "PositionComponent.h"
#include "VelocityComponent.h"

MovementSystem::MovementSystem()
{
dt = 0;
// it is essential to tell the system its dependencies
// only entities containing these components get updated by this system setDependency(PositionComponent::getType());
setDependency(VelocityComponent::getType());
}

void MovementSystem::setTimeDelta(int time)
{
dt = time;
}

void MovementSystem::update(Entity e)
{
VelocityComponent vel = e.getComponent(VelocityComponent::getType());
PositionComponent pos = e.getComponent(PositionComponent::getType());
pos.x += vel.x * dt;
pos.y += vel.y * dt;
e.getComponent(PositionComponent::getType()) = pos;
}


In RenderSystem.h insert:
#ifndef RENDERSYSTEM_H
#define RENDERSYSTEM_H

#include "System.h"

class RenderSystem : public System<RenderSystem>
{
  public:
RenderSystem();
  protected:
void update(Entity e);
};

#endif

In RenderSystem.cpp insert:
#include "RenderSystem.h"
#include "PositionComponent.h"
#include "NameComponent.h"
#include <iostream>

RenderSystem::RenderSystem()
{
setDependency(PositionComponent::getType());
setDependency(NameComponent::getType());
}

void RenderSystem::update(Entity e)
{
PositionComponent pos = e.getComponent(PositionComponent::getType());
std::cout << e.getComponent(NameComponent::getType()).name << " is at ";
std::cout << "(" << pos.x << ", " << pos.y << ")" << std::endl;
}


Now everything is ready to see how an Entity-Component-System works. With our newly created engine, we simulate the race between the Hare and the Hedgehog (and a stone). Create a new file and name it main.cpp

In main.cpp insert following code:
#include "MovementSystem.h"
#include "RenderSystem.h"
#include "PositionComponent.h"
#include "VelocityComponent.h"
#include "NameComponent.h"
#include <iostream>

int main(int argc, char** argv)
{
Entity stone = theEntityManager.createEntity();
stone.setComponent(NameComponent("Stone"));
stone.setComponent(PositionComponent(0, 0));

Entity hedgehog = theEntityManager.createEntity();
hedgehog.setComponent(NameComponent("Hedgehog"));
hedgehog.setComponent(PositionComponent(0, 0));
hedgehog.setComponent(VelocityComponent(1, 1));

Entity hare = theEntityManager.createEntity();
hare.setComponent(NameComponent("Hare"));
hare.setComponent(PositionComponent(0, 0));
hare.setComponent(VelocityComponent(2, 2));

MovementSystem moveSys;
RenderSystem rendSys;

std::cout << "The race begins:" << std::endl;

for (int i = 0; i < 3; ++i)
{
// tell the movement system in every step the required time delta
// to simplify the tutorial, a constant time delta of 1 s is given
moveSys.setTimeDelta(1);
moveSys.run();
rendSys.run();
std::cout << std::endl;
}

std::cout << "the hedgehog stops moving" << std::endl;

// any component can be removed (and again be set) during runtime
// you can see in the output, that the hedgehog no longer gets updated
// by the movement system but still gets rendered by the render system
hedgehog.removeComponent(VelocityComponent::getType());

std::cout << "Hedgehogs wife appears and awaits the hare" << std::endl;
// an entity can be created in any scope
int wifeId;
{
Entity hedgehogsWife = theEntityManager.createEntity();
hedgehogsWife.setComponent(NameComponent("Hedegehogs Wife"));
wifeId = hedgehogsWife.getId();
}
// any entity can be accessed by its id
Entity hedgehogsWife = theEntityManager.getEntity(wifeId);
hedgehogsWife.setComponent(PositionComponent(12, 12));

std::cout << "While nobody was watching, someone has stolen the stone";
std::cout << std::endl << std::endl;

// to destroy an entity call Entity::destroy or use the entity manager
theEntityManager.destroyEntity(stone);
// stone.destroy() does the same

for (int i = 0; i < 3; ++i)
{
moveSys.run();
rendSys.run();
std::cout << std::endl;
}

std::cout << "Hedgehogs wife says hello" << std::endl;
std::cout << "The hare gets angry and runs away";
std::cout << std::endl << std::endl;

// call Entity::hasComponent to check if an entity has a component
if (hare.hasComponent(NameComponent::getType()))
{
// any component (iff set) can be accessed and modified this way
hare.getComponent(NameComponent::getType()).name = "Angry Hare";
}
// to modify a component you can also use Entity::setComponent
// an existent component simply gets overwritten (slower than above)
hare.setComponent(VelocityComponent(1, 3));

// to remove all components of an entity, you can call:
theEntityManager.clearEntity(hedgehog);
// or simply
hedgehogsWife.clear();

for (int i = 1; i < 4; ++i)
{
moveSys.setTimeDelta(i);
moveSys.run();
rendSys.run();
}

std::cout << std::endl << "The End" << std::endl;
}
Compiling and running the code in a terminal results in following output:
The race begins:
Stone is at (0, 0)
Hedgehog is at (1, 1)
Hare is at (2, 2)

Stone is at (0, 0)
Hedgehog is at (2, 2)
Hare is at (4, 4)

Stone is at (0, 0)
Hedgehog is at (3, 3)
Hare is at (6, 6)

the hedgehog stops moving
Hedgehogs wife appears and awaits the hare
While nobody was watching, someone has stolen the stone

Hedegehogs Wife is at (12, 12)
Hedgehog is at (3, 3)
Hare is at (8, 8)

Hedegehogs Wife is at (12, 12)
Hedgehog is at (3, 3)
Hare is at (10, 10)

Hedegehogs Wife is at (12, 12)
Hedgehog is at (3, 3)
Hare is at (12, 12)

Hedgehogs wife says hello
The hare gets angry and runs away

Angry Hare is at (13, 15)
Angry Hare is at (15, 21)
Angry Hare is at (18, 30)

The End