///
/// [EntityFu](https://github.com/NatWeiss/EntityFu)
/// A simple, fast entity component system written in C++.
/// Under the MIT license.
///
#include "EntityFu.h"
#include
using namespace std;
/// Turn this on to have a faster yet riskier ECS.
#define kTrustPointers 0
/// Log macro.
#ifndef LogV
#ifndef NDEBUG
#include
#define LogV(verbosity, minVerbosity, ...) do {if ((verbosity) >= (minVerbosity)) {printf(__VA_ARGS__); printf("\n");}} while (0)
#else
#define LogV(...) do {} while (0)
#endif
#endif
/// Assert macro.
#ifndef Assert
#ifndef NDEBUG
#define Assert(condition, format, ...) {if(!condition) throw format;}
#else
#define Assert(...) do {} while (0)
#endif
#endif
/// Turn this to 1 or 2 to debug the ECS.
/// 1 == log creation, 2 == also log deletion, 3 == also log component add/remove, 4 == also log component totals.
static int verbosity = 0;
/// Static pointers to the ECS data.
static bool* entities = nullptr;
static Entity::Component*** components = nullptr;
static vector* componentEids = nullptr;
static void log(Cid cid)
{
auto n = Entity::count(cid);
auto& eids = Entity::getAll(cid);
if (eids.size() > 0)
LogV(verbosity, 4, " Cid %u has %d entities ranging from %u to %u", cid, n, eids.front(), eids.back());
}
void Entity::alloc()
{
if (components != nullptr)
return;
LogV(verbosity, 1, "Allocing entities");
// allocate entities
entities = new bool[kMaxEntities];
for (Eid eid = 0; eid < kMaxEntities; ++eid)
entities[eid] = false;
// allocate components
auto max = Component::numCids;
components = new Component**[max];
componentEids = new vector[Component::numCids];
for (Cid cid = 0; cid < max; cid++)
{
// allocate component array
components[cid] = new Component*[kMaxEntities];
// zero component pointers
for (Eid eid = 0; eid < kMaxEntities; eid++)
components[cid][eid] = nullptr;
}
}
void Entity::dealloc()
{
LogV(verbosity, 1, "Deallocing entities");
if (components != nullptr)
{
Entity::destroyAll();
for (Cid cid = 0; cid < Component::numCids; cid++)
if (components[cid] != nullptr)
delete [] components[cid];
delete [] components;
}
if (componentEids != nullptr)
delete [] componentEids;
if (entities != nullptr)
delete [] entities;
entities = nullptr;
components = nullptr;
componentEids = nullptr;
}
Eid Entity::create()
{
// auto allocate
Entity::alloc();
Eid eid = 1;
for (; eid < kMaxEntities && entities[eid]; ++eid)
{
}
if (eid < 1 || eid >= kMaxEntities)
{
Assert(false, "Maximum number of entities reached!");
eid = 0;
}
else
{
entities[eid] = true;
LogV(verbosity, 1, "Entity %u created", eid);
}
return eid;
}
void Entity::destroyNow(Eid eid)
{
if (eid == 0)
return;
LogV(verbosity, 1, "Entity %u being destroyed", eid);
for (Cid cid = 0; cid < Component::numCids; cid++)
Entity::removeComponent(cid, eid);
entities[eid] = false;
}
void Entity::destroyAll()
{
unsigned count = 0;
auto oldVerbosity = verbosity;
verbosity = 0;
for (Eid eid = 1; eid < kMaxEntities; ++eid)
{
if (entities[eid])
{
Entity::destroyNow(eid);
count++;
}
}
verbosity = oldVerbosity;
LogV(verbosity, 1, "%u entities destroyed", count);
}
void Entity::addComponent(Cid cid, Eid eid, Component* c)
{
if (c == nullptr)
return;
if (eid >= kMaxEntities || !entities[eid] || cid >= Component::numCids)
{
Assert(false, "Invalid eid %u or cid %u", eid, cid);
return;
}
if (verbosity >= 4)
log(cid);
LogV(verbosity, 3, " Adding component cid %u eid %u (%x)", cid, eid, (int)(long)c);
// if component already added, delete old one
if (components[cid][eid] != nullptr)
Entity::removeComponent(cid, eid);
// pointers to components are stored in the map
// (components must be allocated with new, not stack objects)
components[cid][eid] = c;
// store component eids
componentEids[cid].push_back(eid);
if (verbosity >= 4)
log(cid);
}
void Entity::removeComponent(Cid cid, Eid eid)
{
if (eid >= kMaxEntities || !entities[eid] || cid >= Component::numCids)
{
Assert(false, "Invalid eid %u or cid %u", eid, cid);
return;
}
// get pointer
auto ptr = components[cid][eid];
if (ptr == nullptr)
return;
if (verbosity >= 4)
log(cid);
LogV(verbosity, 3, " Removing component cid %u eid %u (%x)", cid, eid, (int)(long)ptr);
// pointers to components are deleted
delete ptr;
// erase the component pointer
components[cid][eid] = nullptr;
// update component eids
auto& eids = componentEids[cid];
auto it = find(eids.begin(), eids.end(), eid);
if (it != eids.end())
it = eids.erase(it);
if (verbosity >= 4)
log(cid);
}
Entity::Component* Entity::getComponent(Cid cid, Eid eid)
{
#if (kTrustPointers == 0)
if (eid < kMaxEntities && cid < Component::numCids)
{
#endif
return components[cid][eid];
#if (kTrustPointers == 0)
}
return nullptr;
#endif
}
const vector& Entity::getAll(Cid cid)
{
if (componentEids != nullptr && cid < Component::numCids)
return componentEids[cid];
static vector blankEids;
return blankEids;
}
unsigned Entity::count()
{
int ret = 0;
if (entities != nullptr)
{
for (Eid eid = 1; eid < kMaxEntities; ++eid)
if (entities[eid])
++ret;
}
return ret;
}
unsigned Entity::count(Cid cid)
{
return (unsigned)Entity::getAll(cid).size();
}
bool Entity::exists(Eid eid)
{
return entities != nullptr && entities[eid];
}
//
// Entity::Component
//
Entity::Component::~Component()
{
}
bool Entity::Component::full() const
{
return !this->empty();
}
//
// System
//
void System::tick(double fixedDelta)
{
}
void System::animate(double delta, double tickPercent)
{
}