2D_Engine/ECS/ECS.hpp

204 lines
4.9 KiB
C++

/********************\
| Copyright 2024, |
| Ulysse Cura |
\********************/
////////////////////////////
// //
// Definition de l'ECS. //
// //
////////////////////////////
#ifndef ECS_HPP
#define ECS_HPP
#include <algorithm>
#include <array>
#include <bitset>
#include <stdexcept>
#include <iostream>
#include <memory>
#include <vector>
#include "../ChannelManager.hpp"
using std::bitset, std::array, std::vector, std::unique_ptr, std::make_unique, std::move, std::forward, std::runtime_error, std::sort;
class Component;
class Entity;
using ComponentID = std::size_t;
inline ComponentID getComponentTypeID()
{
static ComponentID lastID = 0;
return lastID++;
}
template<typename T> inline ComponentID getComponentTypeID() noexcept
{
static ComponentID typeID = getComponentTypeID();
return typeID;
}
constexpr std::size_t maxComponents = 32;
using ComponentBitSet = bitset<maxComponents>;
using ComponentArray = array<Component*, maxComponents>;
class Component {
public:
Entity *entity;
virtual void init() {}
virtual void update() {}
virtual void draw() {}
virtual ~Component() {}
};
class Entity {
public:
void update()
{
for(auto &c : m_components) c->update();
}
void draw()
{
for(auto &c : m_components) c->draw();
}
bool isActive() const { return m_active; }
void destroy() { m_active = false; }
template <typename T, typename... TArgs>
T& addComponent(TArgs&&... args)
{
if(hasComponent<T>())
{
throw runtime_error("Vous ne pouvez pas mettre deux fois le même composant dans la même entité.\n");
}
// Creation d'un pointeur du type donné avec les arguments necessaires
T *c(new T(forward<TArgs>(args)...));
// On donne au composant une reference de l'entitée
c->entity = this;
// Création d'un unique_ptr et ajout du composant dans le vecteur et array associé
unique_ptr<Component> uPtr {c};
m_components.emplace_back(move(uPtr));
m_componentArray[getComponentTypeID<T>()] = c;
m_componentBitSet[getComponentTypeID<T>()] = true;
// Initialisation du composant
c->init();
return *c;
}
template <typename T>
bool hasComponent() const {
// Vérifier d'abord si un type exact est présent
ComponentID id = getComponentTypeID<T>();
if (m_componentBitSet[id]) {
return true;
}
// Si non, vérifier dynamiquement tous les composants pour un type dérivé
for (const auto& c : m_components) {
if (dynamic_cast<T*>(c.get())) {
return true;
}
}
return false;
}
template <typename T>
T& getComponent() const {
// Vérification rapide via le BitSet et l'accès direct au composant via l'array
ComponentID id = getComponentTypeID<T>();
if (m_componentBitSet[id]) {
return *static_cast<T*>(m_componentArray[id]);
}
// Vérification polymorphique avec dynamic_cast
for (const auto& c : m_components) {
if (T* t = dynamic_cast<T*>(c.get())) {
return *t;
}
}
throw runtime_error("Composant non trouvé.\n");
}
int draw_priority;
private:
bool m_active {true};
vector<unique_ptr<Component>> m_components;
ComponentArray m_componentArray;
ComponentBitSet m_componentBitSet;
};
class Manager {
public:
void update()
{
for(auto &e : m_entities) e->update();
}
void draw()
{
vector<int> drawOrder(m_entities.size());
for(int i {0}; i < static_cast<int>(m_entities.size()); i++) drawOrder[i] = i;
sort(drawOrder.begin(), drawOrder.end(), [this](const int &a, const int &b) {
return m_entities.at(a)->draw_priority < m_entities.at(b)->draw_priority;
});
for(int i {0}; i < static_cast<int>(m_entities.size()); i++)
{
m_entities[drawOrder[i]]->draw();
}
}
void refresh()
{
m_entities.erase(std::remove_if(m_entities.begin(), m_entities.end(),
[](const unique_ptr<Entity> &mEntity) {
return !mEntity->isActive();
}),
m_entities.end());
}
Entity &addEntity()
{
Entity *e = new Entity();
unique_ptr<Entity> uPtr{e};
m_entities.emplace_back(move(uPtr));
return *e;
}
std::size_t getNumberOfEntity() const
{
return m_entities.size();
}
void erase(std::size_t index)
{
m_entities.at(index).get()->destroy();
}
vector<unique_ptr<Entity>> &getEntities() { return m_entities; }
ChannelManager &getChannelManager() { return m_channelManager; }
private:
vector<unique_ptr<Entity>> m_entities;
ChannelManager m_channelManager;
};
#endif