123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /*
- * 2D Game Engine
- * ECS.h:
- * Based on pikuma.com 2D game engine in C++ and Lua course
- * Copyright (c) 2021 986-Studio. All rights reserved.
- *
- * Created by Manoël Trapier on 11/02/2021.
- */
- #ifndef GAMEENGINE_ECS_H
- #define GAMEENGINE_ECS_H
- #include <stdint.h>
- #include <bitset>
- #include <vector>
- #include <unordered_map>
- #include <typeindex>
- #include <set>
- #include <memory>
- #include <deque>
- #include <Logger.h>
- #include <Pool.h>
- const uint32_t MAX_COMPONENTS = 32;
- typedef std::bitset<MAX_COMPONENTS> Signature;
- struct IComponent
- {
- protected:
- static uint32_t nextId;
- };
- template <typename T> class Component: public IComponent
- {
- public:
- static uint32_t getId()
- {
- static auto id = IComponent::nextId++;
- return id;
- }
- };
- class Entity
- {
- private:
- uint32_t id;
- public:
- explicit Entity(uint32_t id): id(id) {};
- Entity(const Entity & entity) = default;
- uint32_t getId() const;
- Entity& operator=(const Entity & other) = default;
- bool operator==(const Entity &other) const { return this->id == other.id; };
- bool operator!=(const Entity &other) const { return this->id != other.id; };
- bool operator>(const Entity &other) const { return this->id > other.id; };
- bool operator<(const Entity &other) const { return this->id < other.id; };
- /* Some syntaxic sugar */
- template <typename T, typename ...TArgs> void addComponent(TArgs&& ...args);
- template <typename T> void removeComponent();
- template <typename T> bool hasComponent();
- template <typename T> T& getComponent();
- class Registry *registry = nullptr;
- void kill();
- void tag(const std::string &tag);
- bool hasTag(const std::string &tag) const;
- void group(const std::string &groupName);
- bool belongsToGroup(const std::string &groupName) const;
- };
- class System
- {
- private:
- Signature componentSignature;
- std::vector<Entity> entities;
- public:
- System() = default;
- ~System() = default;
- void addEntity(Entity entity);
- void removeEntity(Entity entity);
- std::vector<Entity> getSystemEntities() const;
- const Signature& getComponentSignature() const;
- template<typename T> void requireComponent();
- };
- template<typename T> void System::requireComponent()
- {
- const auto componentId = Component<T>::getId();
- this->componentSignature.set(componentId);
- }
- class Registry
- {
- private:
- uint32_t numEntities = 0;
- std::vector<std::shared_ptr<IPool>> componentPools;
- std::vector<Signature> entityComponentSignatures;
- std::unordered_map<std::type_index, std::shared_ptr<System>> systems;
- std::set<Entity> entitiesToBeAdded;
- std::set<Entity> entitiesToBeKilled;
- std::deque<uint32_t> freeIds;
- std::unordered_map<std::string, Entity> entityPerTag;
- std::unordered_map<uint32_t, std::string> tagPerEntity;
- std::unordered_map<std::string, std::set<Entity>> entitiesPerGroup;
- std::unordered_map<uint32_t, std::string> groupPerEntity;
- public:
- Registry() = default;
- ~Registry() = default;
- /* Entities */
- Entity createEntity();
- void killEntity(Entity entity);
- void tagEntity(Entity entity, const std::string &tag);
- bool entityHasTag(Entity entity, const std::string &tag) const;
- Entity getEntityByTag(const std::string &tag) const;
- void removeEntityTag(Entity entity);
- void groupEntity(Entity entity, const std::string &groupName);
- bool entityBelongsToGroup(Entity entity, const std::string &groupName) const;
- std::vector<Entity> getEntitiesByGroup(const std::string &groupName) const;
- void removeEntityGroup(Entity entity);
- void addEntityToSystems(Entity entity);
- void removeEntityFromSystems(Entity entity);
- /* Components */
- template <typename T, typename ...TArgs> void addComponent(Entity entity, TArgs&& ...args);
- template <typename T> void removeComponent(Entity entity);
- template <typename T> bool hasComponent(Entity entity);
- template <typename T> T& getComponent(Entity entity);
- /* Systems */
- template<typename T, typename ...TArgs> void addSystem(TArgs&& ...args);
- template<typename T> void removeSystem();
- template<typename T> bool hasSystem() const;
- template<typename T> T& getSystem() const;
- /* Others */
- void update();
- };
- template<typename T, typename ...TArgs> void Registry::addComponent(Entity entity, TArgs&& ...args)
- {
- const auto componentId = Component<T>::getId();
- const auto entityId = entity.getId();
- std::shared_ptr<PoolToUse<T>> componentPool;
- /* Check the pool is big enough */
- if (componentId >= this->componentPools.size())
- {
- this->componentPools.resize(componentId + 1, nullptr);
- }
- /* Is this component exist in the pool? */
- if (!this->componentPools[componentId])
- {
- this->componentPools[componentId] = std::make_shared<PoolToUse<T>>();
- }
- componentPool = std::static_pointer_cast<PoolToUse<T>>(this->componentPools[componentId]);
- if (entityId >= componentPool->getSize())
- {
- componentPool->resize(this->numEntities);
- }
- T newComponent(std::forward<TArgs>(args)...);
- componentPool->set(entityId, newComponent);
- this->entityComponentSignatures[entityId].set(componentId);
- //Logger::Debug("Component id#%d was added to entity id#%d", componentId, entityId);
- }
- template <typename T> void Registry::removeComponent(Entity entity)
- {
- const auto componentId = Component<T>::getId();
- const auto entityId = entity.getId();
- std::shared_ptr<PoolToUse<T>> componentPool;
- this->entityComponentSignatures[entityId].set(componentId, false);
- componentPool = std::static_pointer_cast<PoolToUse<T>>(this->componentPools[componentId]);
- componentPool.remove(entityId);
- //Logger::Debug("Component id#%d was removed to entity id#%d", componentId, entityId);
- }
- template <typename T> bool Registry::hasComponent(Entity entity)
- {
- const auto componentId = Component<T>::getId();
- const auto entityId = entity.getId();
- return this->entityComponentSignatures[entityId].test(componentId);
- }
- template <typename T> T& Registry::getComponent(Entity entity)
- {
- const auto componentId = Component<T>::getId();
- const auto entityId = entity.getId();
- auto componentPool = std::static_pointer_cast<PoolToUse<T>>(this->componentPools[componentId]);
- return componentPool->get(entityId);
- }
- template<typename T, typename ...TArgs> void Registry::addSystem(TArgs&& ...args)
- {
- std::shared_ptr<T> newSystem = std::make_shared<T>(std::forward<TArgs>(args)...);
- this->systems.insert(std::make_pair(std::type_index(typeid(T)), newSystem));
- }
- template<typename T> void Registry::removeSystem()
- {
- auto system = this->systems.find(std::type_index(typeid(T)));
- this->systems.erase(system);
- }
- template<typename T> bool Registry::hasSystem() const
- {
- return this->systems.find(std::type_index(typeid(T))) != this->systems.end();
- }
- template<typename T> T& Registry::getSystem() const
- {
- auto system = this->systems.find(std::type_index(typeid(T)));
- return *(std::static_pointer_cast<T>(system->second));
- }
- template <typename T, typename ...TArgs> void Entity::addComponent(TArgs&& ...args)
- {
- this->registry->addComponent<T>(*this, std::forward<TArgs>(args)...);
- }
- template <typename T> void Entity::removeComponent()
- {
- this->registry->removeComponent<T>(*this);
- }
- template <typename T> bool Entity::hasComponent()
- {
- return this->registry->hasComponent<T>(*this);
- }
- template <typename T> T& Entity::getComponent()
- {
- return this->registry->getComponent<T>(*this);
- }
- #endif /* GAMEENGINE_ECS_H */
|