/* * 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 #include #include #include #include #include #include #include #include #include const uint32_t MAX_COMPONENTS = 32; typedef std::bitset Signature; struct IComponent { protected: static uint32_t nextId; }; template 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 void addComponent(TArgs&& ...args); template void removeComponent(); template bool hasComponent(); template 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 entities; public: System() = default; ~System() = default; void addEntity(Entity entity); void removeEntity(Entity entity); std::vector getSystemEntities() const; const Signature& getComponentSignature() const; template void requireComponent(); }; template void System::requireComponent() { const auto componentId = Component::getId(); this->componentSignature.set(componentId); } class Registry { private: uint32_t numEntities = 0; std::vector> componentPools; std::vector entityComponentSignatures; std::unordered_map> systems; std::set entitiesToBeAdded; std::set entitiesToBeKilled; std::deque freeIds; std::unordered_map entityPerTag; std::unordered_map tagPerEntity; std::unordered_map> entitiesPerGroup; std::unordered_map 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 getEntitiesByGroup(const std::string &groupName) const; void removeEntityGroup(Entity entity); void addEntityToSystems(Entity entity); void removeEntityFromSystems(Entity entity); /* Components */ template void addComponent(Entity entity, TArgs&& ...args); template void removeComponent(Entity entity); template bool hasComponent(Entity entity); template T& getComponent(Entity entity); /* Systems */ template void addSystem(TArgs&& ...args); template void removeSystem(); template bool hasSystem() const; template T& getSystem() const; /* Others */ void update(); }; template void Registry::addComponent(Entity entity, TArgs&& ...args) { const auto componentId = Component::getId(); const auto entityId = entity.getId(); std::shared_ptr> 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>(); } componentPool = std::static_pointer_cast>(this->componentPools[componentId]); if (entityId >= componentPool->getSize()) { componentPool->resize(this->numEntities); } T newComponent(std::forward(args)...); componentPool->set(entityId, newComponent); this->entityComponentSignatures[entityId].set(componentId); //Logger::Debug("Component id#%d was added to entity id#%d", componentId, entityId); } template void Registry::removeComponent(Entity entity) { const auto componentId = Component::getId(); const auto entityId = entity.getId(); std::shared_ptr> componentPool; this->entityComponentSignatures[entityId].set(componentId, false); componentPool = std::static_pointer_cast>(this->componentPools[componentId]); componentPool.remove(entityId); //Logger::Debug("Component id#%d was removed to entity id#%d", componentId, entityId); } template bool Registry::hasComponent(Entity entity) { const auto componentId = Component::getId(); const auto entityId = entity.getId(); return this->entityComponentSignatures[entityId].test(componentId); } template T& Registry::getComponent(Entity entity) { const auto componentId = Component::getId(); const auto entityId = entity.getId(); auto componentPool = std::static_pointer_cast>(this->componentPools[componentId]); return componentPool->get(entityId); } template void Registry::addSystem(TArgs&& ...args) { std::shared_ptr newSystem = std::make_shared(std::forward(args)...); this->systems.insert(std::make_pair(std::type_index(typeid(T)), newSystem)); } template void Registry::removeSystem() { auto system = this->systems.find(std::type_index(typeid(T))); this->systems.erase(system); } template bool Registry::hasSystem() const { return this->systems.find(std::type_index(typeid(T))) != this->systems.end(); } template T& Registry::getSystem() const { auto system = this->systems.find(std::type_index(typeid(T))); return *(std::static_pointer_cast(system->second)); } template void Entity::addComponent(TArgs&& ...args) { this->registry->addComponent(*this, std::forward(args)...); } template void Entity::removeComponent() { this->registry->removeComponent(*this); } template bool Entity::hasComponent() { return this->registry->hasComponent(*this); } template T& Entity::getComponent() { return this->registry->getComponent(*this); } #endif /* GAMEENGINE_ECS_H */