From 879c2c04d9c3879f871cfe79f9b25fd23c5184b4 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Thu, 11 Jun 2015 11:38:49 -0400 Subject: Wrote EntityManager --- src/entity_manager.h | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 src/entity_manager.h (limited to 'src/entity_manager.h') diff --git a/src/entity_manager.h b/src/entity_manager.h new file mode 100644 index 0000000..74e758c --- /dev/null +++ b/src/entity_manager.h @@ -0,0 +1,279 @@ +#ifndef ENTITY_MANAGER_H_C5832F11 +#define ENTITY_MANAGER_H_C5832F11 + +#include +#include +#include +#include +#include "component.h" + +class EntityManager { + private: + struct EntityData { + int parent = -1; + std::map> components; + }; + + std::map entities; + std::map> cachedChildren; + std::map, std::set> cachedComponents; + + int nextEntityID = 0; + + bool ensureNoParentCycles(int entity, int parent) + { + EntityData& data = entities[parent]; + if (data.parent == entity) + { + return false; + } else if (data.parent == -1) + { + return true; + } + + return ensureNoParentCycles(entity, data.parent); + } + + std::set getEntitiesWithComponents(std::set& componentTypes) + { + if (cachedComponents.count(componentTypes) == 1) + { + return cachedComponents[componentTypes]; + } + + std::set& cache = cachedComponents[componentTypes]; + for (auto& entity : entities) + { + EntityData& data = entity.second; + bool cacheEntity = true; + + for (auto& componentType : componentTypes) + { + if (data.components.count(componentType) == 0) + { + cacheEntity = false; + break; + } + } + + if (cacheEntity) + { + cache.insert(entity.first); + } + } + + return cache; + } + + template std::set getEntitiesWithComponents(std::set& componentTypes) + { + componentTypes.insert(typeid(T)); + + return getEntitiesWithComponents(componentTypes); + } + + public: + EntityManager() = default; + EntityManager(const EntityManager& copy) = delete; + + int emplaceEntity() + { + // Find a suitable entity ID + while ((entities.count(nextEntityID) == 1) && (nextEntityID >= 0)) + { + nextEntityID++; + } + + if (nextEntityID < 0) + { + nextEntityID = 0; + + while ((entities.count(nextEntityID) == 1) && (nextEntityID >= 0)) + { + nextEntityID++; + } + + assert(nextEntityID >= 0); + } + + // Initialize the data + int id = nextEntityID++; + entities[id]; + + return id; + } + + void deleteEntity(int entity) + { + assert(entities.count(entity) == 1); + + EntityData& data = entities[entity]; + + // Destroy the children + std::set children = getChildren(entity); + for (int child : children) + { + EntityData& childData = entities[child]; + childData.parent = -1; + + deleteEntity(child); + } + + // Uncache children + cachedChildren.erase(entity); + + if ((data.parent != -1) && (cachedChildren.count(data.parent) == 1)) + { + cachedChildren[data.parent].erase(entity); + } + + // Uncache components + for (auto& cache : cachedComponents) + { + cache.second.erase(entity); + } + + // Destroy the data + entities.erase(entity); + } + + std::set getChildren(int parent) + { + assert(entities.count(parent) == 1); + + if (cachedChildren.count(parent) == 1) + { + return cachedChildren[parent]; + } + + std::set& cache = cachedChildren[parent]; + for (auto& entity : entities) + { + EntityData& data = entity.second; + if (data.parent == parent) + { + cache.insert(entity.first); + } + } + + return cache; + } + + void setParent(int entity, int parent) + { + assert(entities.count(entity) == 1); + assert(entities.count(parent) == 1); + assert(ensureNoParentCycles(entity, parent)); + + EntityData& data = entities[entity]; + + // Remove from old parent + if (data.parent != -1) + { + if (cachedChildren.count(data.parent) == 1) + { + cachedChildren[data.parent].erase(entity); + } + } + + data.parent = parent; + + // Cache new parent + if (cachedChildren.count(parent) == 1) + { + cachedChildren[parent].insert(entity); + } + } + + void setNoParent(int entity) + { + assert(entities.count(entity) == 1); + + EntityData& data = entities[entity]; + + // Remove from old parent + if (data.parent != -1) + { + if (cachedChildren.count(data.parent) == 1) + { + cachedChildren[data.parent].erase(entity); + } + } + + data.parent = -1; + } + + int getParent(int entity) + { + assert(entities.count(entity) == 1); + + EntityData& data = entities[entity]; + return data.parent; + } + + template T& emplaceComponent(int entity, Args&&... args) + { + assert(entities.count(entity) == 1); + + EntityData& data = entities[entity]; + std::type_index componentType = typeid(T); + + assert(data.components.count(componentType) == 0); + + // Initialize the component + std::unique_ptr ptr = std::unique_ptr(new T(std::forward(args)...)); + T& component = *ptr; + data.components[componentType] = std::move(ptr); + + // Invalidate related caches + std::remove_if(begin(cachedComponents), end(cachedComponents), [&] (std::pair, int>& cache) { + return cache.first.count(componentType) == 1; + }); + + return component; + } + + template void removeComponent(int entity) + { + assert(entities.count(entity) == 1); + + EntityData& data = entities[entity]; + std::type_index componentType = typeid(T); + + assert(data.components.count(componentType) == 1); + + // Destroy the component + data.components.erase(componentType); + + // Uncache the component + for (auto& cache : cachedComponents) + { + if (cache.first.count(componentType) == 1) + { + cache.second.erase(entity); + } + } + } + + template T& getComponent(int entity) + { + assert(entities.count(entity) == 1); + + EntityData& data = entities[entity]; + std::type_index componentType = typeid(T); + + assert(data.components.count(componentType) == 1); + + return *(data.components[componentType]); + } + + template std::set getEntitiesWithComponents() + { + std::set componentTypes; + componentTypes.insert(typeid(T)); + + return getEntitiesWithComponents(componentTypes); + } +}; + +#endif /* end of include guard: ENTITY_MANAGER_H_C5832F11 */ -- cgit 1.4.1