Program Listing for File componentManager.h

Return to documentation for file (PrismEngine/src/componentManager.h)

#pragma once
#include "entity.h"
#include <unordered_map>
#include <set>
#include <typeindex>
#include <memory>
#include <algorithm>
#include <iterator>
#include <vector>

namespace prism {
    namespace scene {
        class ComponentManager
        {
        public:
            ComponentManager() = default;

            template<typename T>
            bool addComponent(Entity entityId, T component) {
                auto& storage = getComponentStorage<T>();
                storage.components[entityId] = std::make_shared<T>(component);
                getEntitiesWith<T>().insert(entityId);
                return true;
            }

            template<typename T>
            bool removeComponent(Entity entityId) {
                auto& storage = getComponentStorage<T>();
                auto it = storage.components.find(entityId);
                if (it == storage.components.end()) return false;

                storage.components.erase(it);
                getEntitiesWith<T>().erase(entityId);
                return true;
            }

            template<typename T>
            T* getComponent(Entity entityId) {
                auto& storage = getComponentStorage<T>();
                auto it = storage.components.find(entityId);
                if (it == storage.components.end()) return nullptr;
                return it->second.get();
            }

            template<typename T>
            const std::set<Entity>& getEntitiesWith() const {
                static const std::set<Entity> emptySet;
                auto typeIndex = std::type_index(typeid(T));
                auto it = entitiesWithComponentSets.find(typeIndex);
                if (it == entitiesWithComponentSets.end()) return emptySet;
                return *it->second;
            }

            template<typename T>
            std::set<Entity>& getEntitiesWith() {
                auto typeIndex = std::type_index(typeid(T));
                if (entitiesWithComponentSets.find(typeIndex) == entitiesWithComponentSets.end()) {
                    entitiesWithComponentSets[typeIndex] = std::make_unique<std::set<Entity>>();
                }
                return *entitiesWithComponentSets[typeIndex];
            }

            template<typename... ComponentTypes>
            std::set<Entity> getEntitiesWithAll() const {
                if constexpr (sizeof...(ComponentTypes) == 0) {
                    return std::set<Entity>();
                }
                else {
                    // Получаем множества для каждого типа компонентов
                    std::vector<const std::set<Entity>*> sets;
                    (sets.push_back(&getEntitiesWith<ComponentTypes>()), ...);

                    // Находим наименьшее множество для оптимизации
                    auto smallestIt = std::min_element(sets.begin(), sets.end(),
                        [](const std::set<Entity>* a, const std::set<Entity>* b) {
                            return a->size() < b->size();
                        });

                    std::set<Entity> result = **smallestIt;

                    // Пересекаем с остальными множествами
                    for (const auto& entitySetPtr : sets) {
                        if (entitySetPtr == *smallestIt) continue;

                        std::set<Entity> temp;
                        std::set_intersection(
                            result.begin(), result.end(),
                            entitySetPtr->begin(), entitySetPtr->end(),
                            std::inserter(temp, temp.begin())
                        );
                        result = std::move(temp);
                    }

                    return result;
                }
            }

            void removeAllComponents(Entity entityId);

        private:
            struct IComponentStorage {
                virtual ~IComponentStorage() = default;

                virtual void removeEntity(Entity entityId) = 0;
            };

            template<typename T>
            struct ComponentStorage : public IComponentStorage {
                std::unordered_map<Entity, std::shared_ptr<T>> components;

                void removeEntity(Entity entityId) override {
                    components.erase(entityId);
                }
            };

            template<typename T>
            ComponentStorage<T>& getComponentStorage() {
                auto typeIndex = std::type_index(typeid(T));
                if (componentStorages.find(typeIndex) == componentStorages.end()) {
                    componentStorages[typeIndex] = std::make_unique<ComponentStorage<T>>();
                }
                return static_cast<ComponentStorage<T>&>(*componentStorages[typeIndex]);
            }

            std::unordered_map<std::type_index, std::unique_ptr<IComponentStorage>> componentStorages;

            std::unordered_map<std::type_index, std::unique_ptr<std::set<Entity>>> entitiesWithComponentSets;
        };
    }
}