├── .gitignore ├── CMakeLists.txt ├── EntitasPP ├── ComponentTypeId.cpp ├── ComponentTypeId.hpp ├── Delegate.hpp ├── Entity.cpp ├── Entity.hpp ├── Group.cpp ├── Group.hpp ├── GroupEventType.hpp ├── GroupObserver.cpp ├── GroupObserver.hpp ├── IComponent.hpp ├── ISystem.hpp ├── Matcher.cpp ├── Matcher.hpp ├── Pool.cpp ├── Pool.hpp ├── ReactiveSystem.cpp ├── ReactiveSystem.hpp ├── SystemContainer.cpp ├── SystemContainer.hpp └── TriggerOnEvent.hpp ├── LICENSE ├── README.md ├── clang_build.sh └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | bin/* 3 | build/* 4 | 5 | *.user 6 | *.user.* 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (c) 2016 Juan Delgado (JuDelCo) 3 | # License: MIT License 4 | # MIT License web page: https://opensource.org/licenses/MIT 5 | 6 | # ---------------------------------------------------------------------------------------------- 7 | 8 | cmake_minimum_required(VERSION 3.0) 9 | 10 | if(NOT CONFIGURED_ONCE) 11 | set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Debug Release RelWithDebInfo MinSizeRel" FORCE) 12 | set(BUILD_CPU_ARCH "x64" CACHE STRING "x86 x64") 13 | endif() 14 | 15 | if( NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug" AND 16 | NOT ${CMAKE_BUILD_TYPE} STREQUAL "Release" AND 17 | NOT ${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo" AND 18 | NOT ${CMAKE_BUILD_TYPE} STREQUAL "MinSizeRel") 19 | message(FATAL_ERROR "Bad CMAKE_BUILD_TYPE variable value (Debug Release RelWithDebInfo MinSizeRel)") 20 | endif() 21 | 22 | if( NOT ${BUILD_CPU_ARCH} STREQUAL "x86" AND 23 | NOT ${BUILD_CPU_ARCH} STREQUAL "x64") 24 | message(FATAL_ERROR "Bad BUILD_CPU_ARCH variable value (x86 x64)") 25 | endif() 26 | 27 | message(STATUS "CMAKE_BUILD_CONFIG: ${CMAKE_BUILD_TYPE} (${BUILD_CPU_ARCH})") 28 | 29 | # ------------------------------------------------------------------------------------------------- 30 | 31 | set(CMAKE_CXX_FLAGS "-Wall -Werror -fmax-errors=5 -std=c++11") 32 | set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf-2 -Og -DDEBUG_ON") # -g 33 | set(CMAKE_CXX_FLAGS_RELEASE "-s -O2") 34 | 35 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin") 36 | set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}) 37 | 38 | set(EXECUTABLE_NAME main_${CMAKE_BUILD_TYPE}_${BUILD_CPU_ARCH}) 39 | file(GLOB_RECURSE ENTITAS_SRC EntitasPP/*.*pp) 40 | 41 | if(${BUILD_CPU_ARCH} STREQUAL "x64") 42 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64") 43 | else() 44 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") 45 | endif() 46 | 47 | # ------------------------------------------------------------------------------------------------- 48 | 49 | project(EntitasPP CXX) 50 | 51 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 52 | 53 | add_executable(${EXECUTABLE_NAME} 54 | main.cpp 55 | ${ENTITAS_SRC} 56 | ) 57 | 58 | # ------------------------------------------------------------------------------------------------- 59 | 60 | set(CONFIGURED_ONCE TRUE CACHE INTERNAL "Flag - CMake has configured at least once") 61 | -------------------------------------------------------------------------------- /EntitasPP/ComponentTypeId.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "ComponentTypeId.hpp" 6 | 7 | namespace EntitasPP 8 | { 9 | unsigned int ComponentTypeId::mCounter = 0; 10 | } 11 | -------------------------------------------------------------------------------- /EntitasPP/ComponentTypeId.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "IComponent.hpp" 8 | #include 9 | 10 | namespace EntitasPP 11 | { 12 | typedef unsigned int ComponentId; 13 | typedef std::vector ComponentIdList; 14 | 15 | struct ComponentTypeId 16 | { 17 | public: 18 | template 19 | static const ComponentId Get() 20 | { 21 | static_assert((std::is_base_of::value && ! std::is_same::value), 22 | "Class type must be derived from IComponent"); 23 | 24 | static ComponentId id = mCounter++; 25 | return id; 26 | } 27 | 28 | static unsigned int Count() 29 | { 30 | return mCounter; 31 | } 32 | 33 | private: 34 | static unsigned int mCounter; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /EntitasPP/Delegate.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | // Based on: 6 | // https://gist.github.com/sim642/4525268 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace EntitasPP 17 | { 18 | template 19 | class Delegate; 20 | 21 | namespace DelegateImpl 22 | { 23 | template 24 | struct Invoker 25 | { 26 | using ReturnType = std::vector; 27 | 28 | public: 29 | static ReturnType Invoke(Delegate &delegate, TArgs... params) 30 | { 31 | std::lock_guard lock(delegate.mMutex); 32 | ReturnType returnValues; 33 | 34 | for (const auto &functionPtr : delegate.mFunctionList) 35 | { 36 | returnValues.push_back((*functionPtr)(params...)); 37 | } 38 | 39 | return returnValues; 40 | } 41 | }; 42 | 43 | template 44 | struct Invoker 45 | { 46 | using ReturnType = void; 47 | 48 | public: 49 | static void Invoke(Delegate &delegate, TArgs... params) 50 | { 51 | std::lock_guard lock(delegate.mMutex); 52 | 53 | for (const auto &functionPtr : delegate.mFunctionList) 54 | { 55 | (*functionPtr)(params...); 56 | } 57 | } 58 | }; 59 | } 60 | 61 | template 62 | class Delegate 63 | { 64 | using Invoker = DelegateImpl::Invoker; 65 | using functionType = std::function; 66 | 67 | friend Invoker; 68 | 69 | public: 70 | Delegate() {} 71 | ~Delegate() {} 72 | 73 | Delegate(const Delegate&) = delete; 74 | const Delegate& operator =(const Delegate&) = delete; 75 | 76 | Delegate& Connect(const functionType &function) 77 | { 78 | std::lock_guard lock(this->mMutex); 79 | 80 | this->mFunctionList.push_back(std::make_shared(function)); 81 | 82 | return *this; 83 | } 84 | 85 | Delegate& Remove(const functionType &function) 86 | { 87 | std::lock_guard lock(this->mMutex); 88 | 89 | this->mFunctionList.remove_if([&](std::shared_ptr &functionPtr) 90 | { 91 | return Hash(function) == Hash(*functionPtr); 92 | }); 93 | 94 | return *this; 95 | } 96 | 97 | inline typename Invoker::ReturnType Invoke(TArgs... args) 98 | { 99 | return Invoker::Invoke(*this, args...); 100 | } 101 | 102 | Delegate& Clear() 103 | { 104 | std::lock_guard lock(this->mMutex); 105 | 106 | this->mFunctionList.clear(); 107 | 108 | return *this; 109 | } 110 | 111 | inline Delegate& operator +=(const functionType &function) 112 | { 113 | return Connect(function); 114 | } 115 | 116 | inline Delegate& operator -=(const functionType &function) 117 | { 118 | return Remove(function); 119 | } 120 | 121 | inline typename Invoker::ReturnType operator ()(TArgs... args) 122 | { 123 | return Invoker::Invoke(*this, args...); 124 | } 125 | 126 | private: 127 | std::mutex mMutex; 128 | std::list> mFunctionList; 129 | 130 | inline constexpr size_t Hash(const functionType &function) const 131 | { 132 | return function.target_type().hash_code(); 133 | } 134 | }; 135 | } 136 | -------------------------------------------------------------------------------- /EntitasPP/Entity.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "Entity.hpp" 6 | 7 | namespace EntitasPP 8 | { 9 | Entity::Entity(std::map>* componentPools) 10 | { 11 | mComponentPools = componentPools; 12 | } 13 | 14 | auto Entity::AddComponent(const ComponentId index, IComponent* component) -> EntityPtr 15 | { 16 | if (! mIsEnabled) 17 | { 18 | throw std::runtime_error("Error, cannot add component to entity, entity has already been destroyed."); 19 | } 20 | 21 | if (HasComponent(index)) 22 | { 23 | throw std::runtime_error("Error, cannot add component to entity, component already exists"); 24 | } 25 | 26 | mComponents[index] = component; 27 | 28 | OnComponentAdded(mInstance.lock(), index, component); 29 | 30 | return mInstance.lock(); 31 | } 32 | 33 | auto Entity::RemoveComponent(const ComponentId index) -> EntityPtr 34 | { 35 | if (! mIsEnabled) 36 | { 37 | throw std::runtime_error("Error, cannot remove component to entity, entity has already been destroyed."); 38 | } 39 | 40 | if (! HasComponent(index)) 41 | { 42 | throw std::runtime_error("Error, cannot remove component to entity, component not exists"); 43 | } 44 | 45 | Replace(index, nullptr); 46 | 47 | return mInstance.lock(); 48 | } 49 | 50 | auto Entity::ReplaceComponent(const ComponentId index, IComponent* component) -> EntityPtr 51 | { 52 | if (! mIsEnabled) 53 | { 54 | throw std::runtime_error("Error, cannot replace component to entity, entity has already been destroyed."); 55 | } 56 | 57 | if (HasComponent(index)) 58 | { 59 | Replace(index, component); 60 | } 61 | else if (component != nullptr) 62 | { 63 | AddComponent(index, component); 64 | } 65 | 66 | return mInstance.lock(); 67 | } 68 | 69 | auto Entity::GetComponent(const ComponentId index) const -> IComponent* 70 | { 71 | if (! HasComponent(index)) 72 | { 73 | throw std::runtime_error("Error, cannot get component from entity, component not exists"); 74 | } 75 | 76 | return mComponents.at(index); 77 | } 78 | 79 | bool Entity::HasComponent(const ComponentId index) const 80 | { 81 | return (mComponents.find(index) != mComponents.end()); 82 | } 83 | 84 | bool Entity::HasComponents(const std::vector& indices) const 85 | { 86 | for(const ComponentId &index : indices) 87 | { 88 | if (! HasComponent(index)) 89 | { 90 | return false; 91 | } 92 | } 93 | 94 | return true; 95 | } 96 | 97 | bool Entity::HasAnyComponent(const std::vector& indices) const 98 | { 99 | for(const ComponentId &index : indices) 100 | { 101 | if (HasComponent(index)) 102 | { 103 | return true; 104 | } 105 | } 106 | 107 | return false; 108 | } 109 | 110 | auto Entity::GetComponentsCount() const -> unsigned int 111 | { 112 | return mComponents.size(); 113 | } 114 | 115 | void Entity::RemoveAllComponents() 116 | { 117 | { 118 | auto componentsIdTemp = std::vector(mComponents.size()); 119 | 120 | for(const auto &pair : mComponents) 121 | { 122 | componentsIdTemp.push_back(pair.first); 123 | } 124 | 125 | while(! mComponents.empty()) 126 | { 127 | Replace(componentsIdTemp.back(), nullptr); 128 | componentsIdTemp.pop_back(); 129 | } 130 | } 131 | } 132 | 133 | auto Entity::GetUuid() const -> const unsigned int 134 | { 135 | return mUuid; 136 | } 137 | 138 | bool Entity::IsEnabled() 139 | { 140 | return mIsEnabled; 141 | } 142 | 143 | bool Entity::operator ==(const EntityPtr& right) const 144 | { 145 | return this->GetUuid() == right->GetUuid(); 146 | } 147 | 148 | bool Entity::operator ==(const Entity right) const 149 | { 150 | return this->GetUuid() == right.GetUuid(); 151 | } 152 | 153 | void Entity::SetInstance(EntityPtr instance) 154 | { 155 | mInstance = std::weak_ptr(instance); 156 | } 157 | 158 | void Entity::Destroy() 159 | { 160 | RemoveAllComponents(); 161 | OnComponentAdded.Clear(); 162 | OnComponentReplaced.Clear(); 163 | OnComponentRemoved.Clear(); 164 | mIsEnabled = false; 165 | } 166 | 167 | auto Entity::GetComponentPool(const ComponentId index) const -> std::stack* 168 | { 169 | return &((*mComponentPools)[index]); 170 | } 171 | 172 | void Entity::Replace(const ComponentId index, IComponent* replacement) 173 | { 174 | auto previousComponent = GetComponent(index); 175 | 176 | if(previousComponent == replacement) 177 | { 178 | OnComponentReplaced(mInstance.lock(), index, previousComponent, replacement); 179 | } 180 | else 181 | { 182 | GetComponentPool(index)->push(previousComponent); 183 | 184 | if(replacement == nullptr) 185 | { 186 | mComponents.erase(index); 187 | OnComponentRemoved(mInstance.lock(), index, previousComponent); 188 | } 189 | else 190 | { 191 | mComponents[index] = replacement; 192 | OnComponentReplaced(mInstance.lock(), index, previousComponent, replacement); 193 | } 194 | } 195 | } 196 | } 197 | 198 | namespace std 199 | { 200 | bool operator ==(weak_ptr left, weak_ptr right) 201 | { 202 | return left.lock().get() == right.lock().get(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /EntitasPP/Entity.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "ComponentTypeId.hpp" 8 | #include "Delegate.hpp" 9 | #include 10 | #include 11 | 12 | namespace EntitasPP 13 | { 14 | class Entity; 15 | typedef std::shared_ptr EntityPtr; 16 | 17 | class Entity 18 | { 19 | friend class Pool; 20 | 21 | public: 22 | Entity(std::map>* componentPools); 23 | 24 | template inline auto Add(TArgs&&... args) -> EntityPtr; 25 | template inline auto Remove() -> EntityPtr; 26 | template inline auto Replace(TArgs&&... args) -> EntityPtr; 27 | template inline auto Refresh() -> EntityPtr; 28 | template inline auto Get() const -> T*; 29 | template inline auto Use() -> T*; 30 | template inline bool Has() const; 31 | 32 | bool HasComponents(const std::vector& indices) const; 33 | bool HasAnyComponent(const std::vector& indices) const; 34 | auto GetComponentsCount() const -> unsigned int; 35 | void RemoveAllComponents(); 36 | auto GetUuid() const -> const unsigned int; 37 | bool IsEnabled(); 38 | 39 | bool operator ==(const EntityPtr& right) const; 40 | bool operator ==(const Entity right) const; 41 | 42 | using EntityChanged = Delegate; 43 | using ComponentReplaced = Delegate; 44 | using EntityReleased = Delegate; 45 | 46 | EntityChanged OnComponentAdded; 47 | ComponentReplaced OnComponentReplaced; 48 | EntityChanged OnComponentRemoved; 49 | EntityReleased OnEntityReleased; 50 | 51 | protected: 52 | void SetInstance(EntityPtr instance); 53 | auto AddComponent(const ComponentId index, IComponent* component) -> EntityPtr; 54 | auto RemoveComponent(const ComponentId index) -> EntityPtr; 55 | auto ReplaceComponent(const ComponentId index, IComponent* component) -> EntityPtr; 56 | auto GetComponent(const ComponentId index) const -> IComponent*; 57 | bool HasComponent(const ComponentId index) const; 58 | void Destroy(); 59 | 60 | template inline auto CreateComponent(TArgs&&... args) -> IComponent*; 61 | 62 | unsigned int mUuid{0}; 63 | bool mIsEnabled = true; 64 | 65 | private: 66 | auto GetComponentPool(const ComponentId index) const -> std::stack*; 67 | void Replace(const ComponentId index, IComponent* replacement); 68 | 69 | std::weak_ptr mInstance; 70 | std::map mComponents; 71 | std::map>* mComponentPools; 72 | }; 73 | 74 | template 75 | auto Entity::CreateComponent(TArgs&&... args) -> IComponent* 76 | { 77 | std::stack* componentPool = GetComponentPool(ComponentTypeId::Get()); 78 | IComponent* component = nullptr; 79 | 80 | if(componentPool->size() > 0) 81 | { 82 | component = componentPool->top(); 83 | componentPool->pop(); 84 | } 85 | else 86 | { 87 | component = new T(); 88 | } 89 | 90 | (static_cast(component))->Reset(std::forward(args)...); 91 | 92 | return component; 93 | } 94 | 95 | template 96 | auto Entity::Add(TArgs&&... args) -> EntityPtr 97 | { 98 | return AddComponent(ComponentTypeId::Get(), CreateComponent(std::forward(args)...)); 99 | } 100 | 101 | template 102 | auto Entity::Remove() -> EntityPtr 103 | { 104 | return RemoveComponent(ComponentTypeId::Get()); 105 | } 106 | 107 | template 108 | auto Entity::Replace(TArgs&&... args) -> EntityPtr 109 | { 110 | return ReplaceComponent(ComponentTypeId::Get(), CreateComponent(std::forward(args)...)); 111 | } 112 | 113 | template 114 | auto Entity::Refresh() -> EntityPtr 115 | { 116 | return ReplaceComponent(ComponentTypeId::Get(), Get()); 117 | } 118 | 119 | template 120 | auto Entity::Get() const -> T* 121 | { 122 | return static_cast(GetComponent(ComponentTypeId::Get())); 123 | } 124 | 125 | template 126 | auto Entity::Use() -> T* 127 | { 128 | Refresh(); 129 | return static_cast(GetComponent(ComponentTypeId::Get())); 130 | } 131 | 132 | template 133 | bool Entity::Has() const 134 | { 135 | return HasComponent(ComponentTypeId::Get()); 136 | } 137 | } 138 | 139 | namespace std 140 | { 141 | template <> 142 | struct hash> 143 | { 144 | std::size_t operator()(const weak_ptr& ptr) const 145 | { 146 | return hash()(ptr.lock()->GetUuid()); 147 | } 148 | }; 149 | 150 | bool operator ==(weak_ptr left, weak_ptr right); 151 | } 152 | -------------------------------------------------------------------------------- /EntitasPP/Group.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "Group.hpp" 6 | #include "Matcher.hpp" 7 | #include "GroupObserver.hpp" 8 | #include 9 | 10 | namespace EntitasPP 11 | { 12 | Group::Group(const Matcher& matcher) : mMatcher(matcher) 13 | { 14 | } 15 | 16 | auto Group::Count() const -> const unsigned int 17 | { 18 | return mEntities.size(); 19 | } 20 | 21 | auto Group::GetEntities() -> std::vector 22 | { 23 | if(mEntitiesCache.empty() && !mEntities.empty()) 24 | { 25 | mEntitiesCache = std::vector(mEntities.begin(), mEntities.end()); 26 | } 27 | 28 | return mEntitiesCache; 29 | } 30 | 31 | auto Group::GetSingleEntity() const -> EntityPtr 32 | { 33 | auto count = Count(); 34 | 35 | if(count == 1) 36 | { 37 | return *(mEntities.begin()); 38 | } 39 | else if(count == 0) 40 | { 41 | return nullptr; 42 | } 43 | else 44 | { 45 | throw std::runtime_error("Error, cannot get the single entity from group. Group contains more than one entity."); 46 | } 47 | 48 | return nullptr; 49 | } 50 | 51 | bool Group::ContainsEntity(const EntityPtr& entity) const 52 | { 53 | return std::find(mEntities.begin(), mEntities.end(), entity) != mEntities.end(); 54 | } 55 | 56 | auto Group::GetMatcher() const -> Matcher 57 | { 58 | return mMatcher; 59 | } 60 | 61 | auto Group::CreateObserver(const GroupEventType eventType) -> std::shared_ptr 62 | { 63 | return std::shared_ptr(new GroupObserver(mInstance.lock(), eventType)); 64 | } 65 | 66 | void Group::SetInstance(std::shared_ptr instance) 67 | { 68 | mInstance = std::weak_ptr(instance); 69 | } 70 | 71 | auto Group::HandleEntity(EntityPtr entity) -> GroupChanged* 72 | { 73 | return mMatcher.Matches(entity) ? AddEntity(entity) : RemoveEntity(entity); 74 | } 75 | 76 | void Group::HandleEntitySilently(EntityPtr entity) 77 | { 78 | if(mMatcher.Matches(entity)) 79 | { 80 | AddEntitySilently(entity); 81 | } 82 | else 83 | { 84 | RemoveEntitySilently(entity); 85 | } 86 | } 87 | 88 | void Group::HandleEntity(EntityPtr entity, ComponentId index, IComponent* component) 89 | { 90 | if(mMatcher.Matches(entity)) 91 | { 92 | AddEntity(entity, index, component); 93 | } 94 | else 95 | { 96 | RemoveEntity(entity, index, component); 97 | } 98 | } 99 | 100 | void Group::UpdateEntity(EntityPtr entity, ComponentId index, IComponent* previousComponent, IComponent* newComponent) 101 | { 102 | if(ContainsEntity(entity)) 103 | { 104 | OnEntityRemoved(mInstance.lock(), entity, index, previousComponent); 105 | OnEntityAdded(mInstance.lock(), entity, index, newComponent); 106 | OnEntityUpdated(mInstance.lock(), entity, index, previousComponent, newComponent); 107 | } 108 | } 109 | 110 | void Group::RemoveAllEventHandlers() 111 | { 112 | OnEntityAdded.Clear(); 113 | OnEntityRemoved.Clear(); 114 | OnEntityUpdated.Clear(); 115 | } 116 | 117 | bool Group::AddEntitySilently(EntityPtr entity) 118 | { 119 | if(mEntities.insert(entity).second) 120 | { 121 | mEntitiesCache.clear(); 122 | return true; 123 | } 124 | 125 | return false; 126 | } 127 | 128 | void Group::AddEntity(EntityPtr entity, ComponentId index, IComponent* component) 129 | { 130 | if(AddEntitySilently(entity)) 131 | { 132 | OnEntityAdded(mInstance.lock(), entity, index, component); 133 | } 134 | } 135 | 136 | auto Group::AddEntity(EntityPtr entity) -> GroupChanged* 137 | { 138 | return AddEntitySilently(entity) ? &OnEntityAdded : nullptr; 139 | } 140 | 141 | bool Group::RemoveEntitySilently(EntityPtr entity) 142 | { 143 | if(mEntities.erase(entity)) 144 | { 145 | mEntitiesCache.clear(); 146 | return true; 147 | } 148 | 149 | return false; 150 | } 151 | 152 | void Group::RemoveEntity(EntityPtr entity, ComponentId index, IComponent* component) 153 | { 154 | if(RemoveEntitySilently(entity)) 155 | { 156 | OnEntityRemoved(mInstance.lock(), entity, index, component); 157 | } 158 | } 159 | 160 | auto Group::RemoveEntity(EntityPtr entity) -> GroupChanged* 161 | { 162 | return RemoveEntitySilently(entity) ? &OnEntityRemoved : nullptr; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /EntitasPP/Group.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "Entity.hpp" 8 | #include "Matcher.hpp" 9 | #include "GroupEventType.hpp" 10 | #include 11 | 12 | namespace EntitasPP 13 | { 14 | class GroupObserver; 15 | 16 | class Group 17 | { 18 | friend class Pool; 19 | 20 | public: 21 | Group(const Matcher& matcher); 22 | auto Count() const -> const unsigned int; 23 | auto GetEntities() -> std::vector; 24 | auto GetSingleEntity() const -> EntityPtr; 25 | bool ContainsEntity(const EntityPtr& entity) const; 26 | auto GetMatcher() const -> Matcher; 27 | auto CreateObserver(const GroupEventType eventType) -> std::shared_ptr; 28 | 29 | using GroupChanged = Delegate group, EntityPtr entity, ComponentId index, IComponent* component)>; 30 | using GroupUpdated = Delegate group, EntityPtr entity, ComponentId index, IComponent* previousComponent, IComponent* newComponent)>; 31 | 32 | GroupChanged OnEntityAdded; 33 | GroupUpdated OnEntityUpdated; 34 | GroupChanged OnEntityRemoved; 35 | 36 | protected: 37 | void SetInstance(std::shared_ptr instance); 38 | auto HandleEntity(EntityPtr entity) -> GroupChanged*; 39 | void HandleEntitySilently(EntityPtr entity); 40 | void HandleEntity(EntityPtr entity, ComponentId index, IComponent* component); 41 | void UpdateEntity(EntityPtr entity, ComponentId index, IComponent* previousComponent, IComponent* newComponent); 42 | void RemoveAllEventHandlers(); 43 | 44 | private: 45 | bool AddEntitySilently(EntityPtr entity); 46 | void AddEntity(EntityPtr entity, ComponentId index, IComponent* component); 47 | auto AddEntity(EntityPtr entity) -> GroupChanged*; 48 | bool RemoveEntitySilently(EntityPtr entity); 49 | void RemoveEntity(EntityPtr entity, ComponentId index, IComponent* component); 50 | auto RemoveEntity(EntityPtr entity) -> GroupChanged*; 51 | 52 | std::weak_ptr mInstance; 53 | Matcher mMatcher; 54 | std::unordered_set mEntities; 55 | std::vector mEntitiesCache; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /EntitasPP/GroupEventType.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | namespace EntitasPP 8 | { 9 | enum class GroupEventType 10 | { 11 | OnEntityAdded, 12 | OnEntityRemoved, 13 | OnEntityAddedOrRemoved 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /EntitasPP/GroupObserver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "GroupObserver.hpp" 6 | #include "Group.hpp" 7 | #include 8 | 9 | namespace EntitasPP 10 | { 11 | GroupObserver::GroupObserver(std::weak_ptr group, const GroupEventType eventType) 12 | { 13 | mGroups.push_back(group); 14 | mEventTypes.push_back(eventType); 15 | mAddEntityCache = std::bind(&GroupObserver::AddEntity, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); 16 | } 17 | 18 | GroupObserver::GroupObserver(std::vector> groups, std::vector eventTypes) 19 | { 20 | mGroups = groups; 21 | mEventTypes = eventTypes; 22 | mAddEntityCache = std::bind(&GroupObserver::AddEntity, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); 23 | 24 | if(groups.size() != eventTypes.size()) 25 | { 26 | throw std::runtime_error("Error, group and eventType vector counts must be equal"); 27 | } 28 | 29 | Activate(); 30 | } 31 | 32 | GroupObserver::~GroupObserver() 33 | { 34 | Deactivate(); 35 | } 36 | 37 | void GroupObserver::Activate() 38 | { 39 | for(unsigned int i = 0, groupCount = mGroups.size(); i < groupCount; ++i) 40 | { 41 | auto g = mGroups[i].lock(); 42 | auto eventType = mEventTypes[i]; 43 | 44 | if(eventType == GroupEventType::OnEntityAdded) 45 | { 46 | g->OnEntityAdded -= mAddEntityCache; 47 | g->OnEntityAdded += mAddEntityCache; 48 | } 49 | else if(eventType == GroupEventType::OnEntityRemoved) 50 | { 51 | g->OnEntityRemoved -= mAddEntityCache; 52 | g->OnEntityRemoved += mAddEntityCache; 53 | } 54 | else if(eventType == GroupEventType::OnEntityAddedOrRemoved) 55 | { 56 | g->OnEntityAdded -= mAddEntityCache; 57 | g->OnEntityAdded += mAddEntityCache; 58 | 59 | g->OnEntityRemoved -= mAddEntityCache; 60 | g->OnEntityRemoved += mAddEntityCache; 61 | } 62 | } 63 | } 64 | 65 | void GroupObserver::Deactivate() 66 | { 67 | for(unsigned int i = 0, groupCount = mGroups.size(); i < groupCount; ++i) 68 | { 69 | if (!mGroups[i].expired()) 70 | { 71 | auto g = mGroups[i].lock(); 72 | 73 | g->OnEntityAdded -= mAddEntityCache; 74 | g->OnEntityRemoved -= mAddEntityCache; 75 | } 76 | } 77 | 78 | ClearCollectedEntities(); 79 | } 80 | 81 | auto GroupObserver::GetCollectedEntities() -> std::unordered_set 82 | { 83 | return mCollectedEntities; 84 | } 85 | 86 | void GroupObserver::ClearCollectedEntities() 87 | { 88 | mCollectedEntities.clear(); 89 | } 90 | 91 | void GroupObserver::AddEntity(std::shared_ptr group, EntityPtr entity, ComponentId index, IComponent* component) 92 | { 93 | mCollectedEntities.insert(entity); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /EntitasPP/GroupObserver.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "ComponentTypeId.hpp" 8 | #include "Entity.hpp" 9 | #include "GroupEventType.hpp" 10 | #include 11 | #include 12 | #include 13 | 14 | namespace EntitasPP 15 | { 16 | class Group; 17 | 18 | class GroupObserver 19 | { 20 | public: 21 | GroupObserver(std::weak_ptr group, const GroupEventType eventType); 22 | GroupObserver(std::vector> groups, std::vector eventTypes); 23 | ~GroupObserver(); 24 | 25 | void Activate(); 26 | void Deactivate(); 27 | auto GetCollectedEntities() -> std::unordered_set; 28 | void ClearCollectedEntities(); 29 | 30 | private: 31 | void AddEntity(std::shared_ptr group, EntityPtr entity, ComponentId index, IComponent* component); 32 | 33 | std::unordered_set mCollectedEntities; 34 | std::vector> mGroups; 35 | std::vector mEventTypes; 36 | std::function, EntityPtr, ComponentId, IComponent*)> mAddEntityCache; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /EntitasPP/IComponent.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | namespace EntitasPP 8 | { 9 | class IComponent 10 | { 11 | friend class Entity; 12 | 13 | protected: 14 | IComponent() = default; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /EntitasPP/ISystem.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "Entity.hpp" 8 | #include "Matcher.hpp" 9 | #include "TriggerOnEvent.hpp" 10 | #include 11 | 12 | namespace EntitasPP 13 | { 14 | class Pool; 15 | 16 | class ISystem 17 | { 18 | protected: 19 | ISystem() = default; 20 | 21 | public: 22 | virtual ~ISystem() = default; 23 | }; 24 | 25 | class ISetPoolSystem 26 | { 27 | protected: 28 | ISetPoolSystem() = default; 29 | 30 | public: 31 | virtual ~ISetPoolSystem() = default; 32 | 33 | virtual void SetPool(Pool* pool) = 0; 34 | }; 35 | 36 | class IInitializeSystem 37 | { 38 | protected: 39 | IInitializeSystem() = default; 40 | 41 | public: 42 | virtual ~IInitializeSystem() = default; 43 | 44 | virtual void Initialize() = 0; 45 | }; 46 | 47 | class IExecuteSystem : public ISystem 48 | { 49 | protected: 50 | IExecuteSystem() = default; 51 | 52 | public: 53 | virtual ~IExecuteSystem() = default; 54 | 55 | virtual void Execute() = 0; 56 | }; 57 | 58 | class IFixedExecuteSystem : public ISystem 59 | { 60 | protected: 61 | IFixedExecuteSystem() = default; 62 | 63 | public: 64 | virtual ~IFixedExecuteSystem() = default; 65 | 66 | virtual void FixedExecute() = 0; 67 | }; 68 | 69 | class IReactiveExecuteSystem : public ISystem 70 | { 71 | protected: 72 | IReactiveExecuteSystem() = default; 73 | 74 | public: 75 | virtual ~IReactiveExecuteSystem() = default; 76 | 77 | virtual void Execute(std::vector entities) = 0; 78 | }; 79 | 80 | class IReactiveSystem : public IReactiveExecuteSystem 81 | { 82 | public: 83 | virtual ~IReactiveSystem() 84 | { 85 | delete trigger; 86 | }; 87 | 88 | const TriggerOnEvent* trigger; 89 | }; 90 | 91 | class IMultiReactiveSystem : public IReactiveExecuteSystem 92 | { 93 | public: 94 | virtual ~IMultiReactiveSystem() = default; 95 | 96 | std::vector triggers; 97 | }; 98 | 99 | class IEnsureComponents 100 | { 101 | protected: 102 | IEnsureComponents() = default; 103 | 104 | public: 105 | Matcher ensureComponents; 106 | }; 107 | 108 | class IExcludeComponents 109 | { 110 | protected: 111 | IExcludeComponents() = default; 112 | 113 | public: 114 | Matcher excludeComponents; 115 | }; 116 | 117 | class IClearReactiveSystem 118 | { 119 | protected: 120 | IClearReactiveSystem() = default; 121 | }; 122 | } 123 | -------------------------------------------------------------------------------- /EntitasPP/Matcher.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "Matcher.hpp" 6 | #include "TriggerOnEvent.hpp" 7 | #include 8 | 9 | namespace EntitasPP 10 | { 11 | auto Matcher::AllOf(const ComponentIdList indices) -> const Matcher 12 | { 13 | auto matcher = Matcher(); 14 | matcher.mAllOfIndices = DistinctIndices(indices); 15 | matcher.CalculateHash(); 16 | 17 | return matcher; 18 | } 19 | 20 | auto Matcher::AllOf(const MatcherList matchers) -> const Matcher 21 | { 22 | return Matcher::AllOf(MergeIndices(matchers)); 23 | } 24 | 25 | auto Matcher::AnyOf(const ComponentIdList indices) -> const Matcher 26 | { 27 | auto matcher = Matcher(); 28 | matcher.mAnyOfIndices = DistinctIndices(indices); 29 | matcher.CalculateHash(); 30 | 31 | return matcher; 32 | } 33 | 34 | auto Matcher::AnyOf(const MatcherList matchers) -> const Matcher 35 | { 36 | return Matcher::AnyOf(MergeIndices(matchers)); 37 | } 38 | 39 | auto Matcher::NoneOf(const ComponentIdList indices) -> const Matcher 40 | { 41 | auto matcher = Matcher(); 42 | matcher.mNoneOfIndices = DistinctIndices(indices); 43 | matcher.CalculateHash(); 44 | 45 | return matcher; 46 | } 47 | 48 | auto Matcher::NoneOf(const MatcherList matchers) -> const Matcher 49 | { 50 | return Matcher::NoneOf(MergeIndices(matchers)); 51 | } 52 | 53 | bool Matcher::IsEmpty() const 54 | { 55 | return (mAllOfIndices.empty() && mAnyOfIndices.empty() && mNoneOfIndices.empty()); 56 | } 57 | 58 | bool Matcher::Matches(const EntityPtr& entity) 59 | { 60 | auto matchesAllOf = mAllOfIndices.empty() || entity->HasComponents(mAllOfIndices); 61 | auto matchesAnyOf = mAnyOfIndices.empty() || entity->HasAnyComponent(mAnyOfIndices); 62 | auto matchesNoneOf = mNoneOfIndices.empty() || ! entity->HasAnyComponent(mNoneOfIndices); 63 | 64 | return matchesAllOf && matchesAnyOf && matchesNoneOf; 65 | } 66 | 67 | auto Matcher::GetIndices() -> const ComponentIdList 68 | { 69 | if(mIndices.empty()) 70 | { 71 | mIndices = MergeIndices(); 72 | } 73 | 74 | return mIndices; 75 | } 76 | 77 | auto Matcher::GetAllOfIndices() const -> const ComponentIdList 78 | { 79 | return mAllOfIndices; 80 | } 81 | 82 | auto Matcher::GetAnyOfIndices() const -> const ComponentIdList 83 | { 84 | return mAnyOfIndices; 85 | } 86 | 87 | auto Matcher::GetNoneOfIndices() const -> const ComponentIdList 88 | { 89 | return mNoneOfIndices; 90 | } 91 | 92 | auto Matcher::GetHashCode() const -> unsigned int 93 | { 94 | return mCachedHash; 95 | } 96 | 97 | bool Matcher::CompareIndices(const Matcher& matcher) const 98 | { 99 | if(matcher.IsEmpty()) 100 | { 101 | return false; 102 | } 103 | 104 | auto leftIndices = this->MergeIndices(); 105 | auto rightIndices = matcher.MergeIndices(); 106 | 107 | if(leftIndices.size() != rightIndices.size()) 108 | { 109 | return false; 110 | } 111 | 112 | for(unsigned int i = 0, count = leftIndices.size(); i < count; ++i) 113 | { 114 | if(leftIndices[i] != rightIndices[i]) 115 | { 116 | return false; 117 | } 118 | } 119 | 120 | return true; 121 | } 122 | 123 | auto Matcher::OnEntityAdded() -> const TriggerOnEvent* 124 | { 125 | return new TriggerOnEvent(*this, GroupEventType::OnEntityAdded); 126 | } 127 | 128 | auto Matcher::OnEntityRemoved() -> const TriggerOnEvent* 129 | { 130 | return new TriggerOnEvent(*this, GroupEventType::OnEntityRemoved); 131 | } 132 | 133 | auto Matcher::OnEntityAddedOrRemoved() -> const TriggerOnEvent* 134 | { 135 | return new TriggerOnEvent(*this, GroupEventType::OnEntityAddedOrRemoved); 136 | } 137 | 138 | bool Matcher::operator ==(const Matcher right) const 139 | { 140 | return this->GetHashCode() == right.GetHashCode() && this->CompareIndices(right); 141 | } 142 | 143 | auto Matcher::MergeIndices() const -> ComponentIdList 144 | { 145 | auto indicesList = ComponentIdList(); 146 | indicesList.reserve(mAllOfIndices.size() + mAnyOfIndices.size() + mNoneOfIndices.size()); 147 | 148 | for(const auto &id : mAllOfIndices) 149 | { 150 | indicesList.push_back(id); 151 | } 152 | 153 | for(const auto &id : mAnyOfIndices) 154 | { 155 | indicesList.push_back(id); 156 | } 157 | 158 | for(const auto &id : mNoneOfIndices) 159 | { 160 | indicesList.push_back(id); 161 | } 162 | 163 | return DistinctIndices(indicesList); 164 | } 165 | 166 | void Matcher::CalculateHash() 167 | { 168 | unsigned int hash = typeid(Matcher).hash_code(); 169 | 170 | hash = ApplyHash(hash, mAllOfIndices, 3, 53); 171 | hash = ApplyHash(hash, mAnyOfIndices, 307, 367); 172 | hash = ApplyHash(hash, mNoneOfIndices, 647, 683); 173 | 174 | mCachedHash = hash; 175 | } 176 | 177 | auto Matcher::ApplyHash(unsigned int hash, const ComponentIdList indices, int i1, int i2) const -> unsigned int 178 | { 179 | if (indices.size() > 0) 180 | { 181 | for (int i = 0, indicesLength = indices.size(); i < indicesLength; i++) 182 | { 183 | hash ^= indices[i] * i1; 184 | } 185 | 186 | hash ^= indices.size() * i2; 187 | } 188 | 189 | return hash; 190 | } 191 | 192 | auto Matcher::MergeIndices(MatcherList matchers) -> ComponentIdList 193 | { 194 | unsigned int totalIndices = 0; 195 | 196 | for(auto &matcher : matchers) 197 | { 198 | totalIndices += matcher.GetIndices().size(); 199 | } 200 | 201 | auto indices = ComponentIdList(); 202 | indices.reserve(totalIndices); 203 | 204 | for(auto &matcher : matchers) 205 | { 206 | for(const auto &id : matcher.GetIndices()) 207 | { 208 | indices.push_back(id); 209 | } 210 | } 211 | 212 | return indices; 213 | } 214 | 215 | auto Matcher::DistinctIndices(ComponentIdList indices) -> ComponentIdList 216 | { 217 | std::sort(indices.begin(), indices.end()); 218 | indices.erase(std::unique(indices.begin(), indices.end()), indices.end()); 219 | 220 | return indices; 221 | 222 | // Old Code (delete!) 223 | /*auto indicesSet = unordered_set(indices.begin(), indices.end()); 224 | 225 | auto uniqueIndices = ComponentIdList(); 226 | uniqueIndices.reserve(indicesSet.size()); 227 | 228 | for(const auto &id : indicesSet) 229 | { 230 | uniqueIndices.push_back(id); 231 | } 232 | 233 | std::sort(uniqueIndices.begin(), uniqueIndices.end(), [](unsigned int a, unsigned int b) { 234 | return b < a; 235 | }); 236 | 237 | return uniqueIndices;*/ 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /EntitasPP/Matcher.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "Entity.hpp" 8 | 9 | namespace EntitasPP 10 | { 11 | class Matcher; 12 | class TriggerOnEvent; 13 | typedef std::vector MatcherList; 14 | 15 | class Matcher 16 | { 17 | public: 18 | Matcher() = default; 19 | static auto AllOf(const ComponentIdList indices) -> const Matcher; 20 | static auto AllOf(const MatcherList matchers) -> const Matcher; 21 | static auto AnyOf(const ComponentIdList indices) -> const Matcher; 22 | static auto AnyOf(const MatcherList matchers) -> const Matcher; 23 | static auto NoneOf(const ComponentIdList indices) -> const Matcher; 24 | static auto NoneOf(const MatcherList matchers) -> const Matcher; 25 | 26 | bool IsEmpty() const; 27 | bool Matches(const EntityPtr& entity); 28 | auto GetIndices() -> const ComponentIdList; 29 | auto GetAllOfIndices() const -> const ComponentIdList; 30 | auto GetAnyOfIndices() const -> const ComponentIdList; 31 | auto GetNoneOfIndices() const -> const ComponentIdList; 32 | 33 | auto GetHashCode() const -> unsigned int; 34 | bool CompareIndices(const Matcher& matcher) const; 35 | 36 | auto OnEntityAdded() -> const TriggerOnEvent*; 37 | auto OnEntityRemoved() -> const TriggerOnEvent*; 38 | auto OnEntityAddedOrRemoved() -> const TriggerOnEvent*; 39 | 40 | bool operator ==(const Matcher right) const; 41 | 42 | protected: 43 | void CalculateHash(); 44 | 45 | ComponentIdList mIndices; 46 | ComponentIdList mAllOfIndices; 47 | ComponentIdList mAnyOfIndices; 48 | ComponentIdList mNoneOfIndices; 49 | 50 | private: 51 | auto ApplyHash(unsigned int hash, const ComponentIdList indices, int i1, int i2) const -> unsigned int; 52 | auto MergeIndices() const -> ComponentIdList; 53 | static auto MergeIndices(MatcherList matchers) -> ComponentIdList; 54 | static auto DistinctIndices(ComponentIdList indices) -> ComponentIdList; 55 | 56 | unsigned int mCachedHash{0}; 57 | }; 58 | } 59 | 60 | namespace std 61 | { 62 | template <> 63 | struct hash 64 | { 65 | std::size_t operator()(const EntitasPP::Matcher& matcher) const 66 | { 67 | return hash()(matcher.GetHashCode()); 68 | } 69 | }; 70 | } 71 | 72 | namespace 73 | { 74 | #define FUNC_1(MODIFIER, X) MODIFIER(X) 75 | #define FUNC_2(MODIFIER, X, ...) MODIFIER(X), FUNC_1(MODIFIER, __VA_ARGS__) 76 | #define FUNC_3(MODIFIER, X, ...) MODIFIER(X), FUNC_2(MODIFIER, __VA_ARGS__) 77 | #define FUNC_4(MODIFIER, X, ...) MODIFIER(X), FUNC_3(MODIFIER, __VA_ARGS__) 78 | #define FUNC_5(MODIFIER, X, ...) MODIFIER(X), FUNC_4(MODIFIER, __VA_ARGS__) 79 | #define FUNC_6(MODIFIER, X, ...) MODIFIER(X), FUNC_5(MODIFIER, __VA_ARGS__) 80 | #define GET_MACRO(_1, _2, _3, _4, _5, _6, NAME,...) NAME 81 | #define FOR_EACH(MODIFIER,...) GET_MACRO(__VA_ARGS__, FUNC_6, FUNC_5, FUNC_4, FUNC_3, FUNC_2, FUNC_1)(MODIFIER, __VA_ARGS__) 82 | 83 | #define COMPONENT_GET_TYPE_ID(COMPONENT_CLASS) EntitasPP::ComponentTypeId::Get() 84 | #define Matcher_AllOf(...) ((EntitasPP::Matcher)EntitasPP::Matcher::AllOf(std::vector({ FOR_EACH(COMPONENT_GET_TYPE_ID, __VA_ARGS__) }))) 85 | #define Matcher_AnyOf(...) ((EntitasPP::Matcher)EntitasPP::Matcher::AnyOf(std::vector({ FOR_EACH(COMPONENT_GET_TYPE_ID, __VA_ARGS__) }))) 86 | #define Matcher_NoneOf(...) ((EntitasPP::Matcher)EntitasPP::Matcher::NoneOf(std::vector({ FOR_EACH(COMPONENT_GET_TYPE_ID, __VA_ARGS__) }))) 87 | } 88 | -------------------------------------------------------------------------------- /EntitasPP/Pool.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "Pool.hpp" 6 | #include "Entity.hpp" 7 | #include "ISystem.hpp" 8 | #include "ReactiveSystem.hpp" 9 | #include 10 | 11 | namespace EntitasPP 12 | { 13 | Pool::Pool(const unsigned int startCreationIndex) 14 | { 15 | mCreationIndex = startCreationIndex; 16 | mOnEntityReleasedCache = std::bind(&Pool::OnEntityReleased, this, std::placeholders::_1); 17 | mOnComponentAddedCache = std::bind(&Pool::UpdateGroupsComponentAddedOrRemoved, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); 18 | mOnComponentRemovedCache = std::bind(&Pool::UpdateGroupsComponentAddedOrRemoved, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); 19 | mOnComponentReplacedCache = std::bind(&Pool::UpdateGroupsComponentReplaced, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); 20 | } 21 | 22 | Pool::~Pool() 23 | { 24 | Reset(); 25 | 26 | // TODO: Components don't get destroyed. 27 | 28 | if(! mRetainedEntities.empty()) 29 | { 30 | // Warning, some entities remain undestroyed in the pool destruction !" 31 | } 32 | 33 | while(! mReusableEntities.empty()) 34 | { 35 | delete mReusableEntities.top(); 36 | mReusableEntities.pop(); 37 | } 38 | 39 | for(auto &pair : mComponentPools) 40 | { 41 | auto componentPool = pair.second; 42 | 43 | while(! componentPool.empty()) 44 | { 45 | delete componentPool.top(); 46 | componentPool.pop(); 47 | } 48 | } 49 | } 50 | 51 | auto Pool::CreateEntity() -> EntityPtr 52 | { 53 | EntityPtr entity; 54 | 55 | if(mReusableEntities.size() > 0) 56 | { 57 | entity = EntityPtr(mReusableEntities.top()); 58 | mReusableEntities.pop(); 59 | } 60 | else 61 | { 62 | entity = EntityPtr(new Entity(&mComponentPools), [](Entity* entity) 63 | { 64 | entity->OnEntityReleased(entity); 65 | }); 66 | } 67 | 68 | entity->SetInstance(entity); 69 | entity->mIsEnabled = true; 70 | entity->mUuid = mCreationIndex++; 71 | 72 | mEntities.insert(entity); 73 | mEntitiesCache.clear(); 74 | 75 | entity->OnComponentAdded += mOnComponentAddedCache; 76 | entity->OnComponentRemoved += mOnComponentRemovedCache; 77 | entity->OnComponentReplaced += mOnComponentReplacedCache; 78 | 79 | entity->OnEntityReleased.Clear(); 80 | entity->OnEntityReleased += mOnEntityReleasedCache; 81 | 82 | OnEntityCreated(this, entity); 83 | 84 | return entity; 85 | } 86 | 87 | bool Pool::HasEntity(const EntityPtr& entity) const 88 | { 89 | return std::find(mEntities.begin(), mEntities.end(), std::weak_ptr(entity)) != mEntities.end(); 90 | } 91 | 92 | void Pool::DestroyEntity(EntityPtr entity) 93 | { 94 | auto removed = mEntities.erase(entity); 95 | 96 | if (! removed) 97 | { 98 | throw std::runtime_error("Error, cannot destroy entity. Pool does not contain entity."); 99 | } 100 | 101 | mEntitiesCache.clear(); 102 | 103 | OnEntityWillBeDestroyed(this, entity); 104 | entity->Destroy(); 105 | OnEntityDestroyed(this, entity); 106 | 107 | if (entity.use_count() == 1) 108 | { 109 | entity->OnEntityReleased -= mOnEntityReleasedCache; 110 | mReusableEntities.push(entity.get()); 111 | } 112 | else 113 | { 114 | mRetainedEntities.insert(entity.get()); 115 | } 116 | } 117 | 118 | void Pool::DestroyAllEntities() 119 | { 120 | { 121 | auto entitiesTemp = std::vector(mEntities.begin(), mEntities.end()); 122 | 123 | while(! mEntities.empty()) 124 | { 125 | DestroyEntity(entitiesTemp.back()); 126 | entitiesTemp.pop_back(); 127 | } 128 | } 129 | 130 | mEntities.clear(); 131 | 132 | if (! mRetainedEntities.empty()) 133 | { 134 | // Try calling pool.ClearGroups() and systemContainer.ClearReactiveSystems() before calling pool.DestroyAllEntities() to avoid memory leaks 135 | throw std::runtime_error("Error, pool detected retained entities although all entities got destroyed. Did you release all entities?"); 136 | } 137 | } 138 | 139 | auto Pool::GetEntities() -> std::vector 140 | { 141 | if(mEntitiesCache.empty()) 142 | { 143 | mEntitiesCache = std::vector(mEntities.begin(), mEntities.end()); 144 | } 145 | 146 | return mEntitiesCache; 147 | } 148 | 149 | auto Pool::GetEntities(const Matcher matcher) -> std::vector 150 | { 151 | return GetGroup(std::move(matcher))->GetEntities(); 152 | } 153 | 154 | auto Pool::GetGroup(Matcher matcher) -> std::shared_ptr 155 | { 156 | std::shared_ptr group = nullptr; 157 | 158 | auto it = mGroups.find(matcher); 159 | 160 | if (it == mGroups.end()) 161 | { 162 | group = std::shared_ptr(new Group(matcher)); 163 | group->SetInstance(group); 164 | 165 | auto entities = GetEntities(); 166 | 167 | for (int i = 0, entitiesLength = entities.size(); i < entitiesLength; i++) 168 | { 169 | group->HandleEntitySilently(entities[i]); 170 | } 171 | 172 | mGroups[group->GetMatcher()] = group; 173 | 174 | for (int i = 0, indicesLength = matcher.GetIndices().size(); i < indicesLength; i++) 175 | { 176 | mGroupsForIndex[matcher.GetIndices()[i]].push_back(group); 177 | } 178 | 179 | OnGroupCreated(this, group); 180 | } 181 | else 182 | { 183 | group = it->second; 184 | } 185 | 186 | return group; 187 | } 188 | 189 | void Pool::ClearGroups() 190 | { 191 | for (const auto &it : mGroups) 192 | { 193 | it.second->RemoveAllEventHandlers(); 194 | OnGroupCleared(this, it.second); 195 | } 196 | 197 | mGroups.clear(); 198 | 199 | for (auto &pair : mGroupsForIndex) 200 | { 201 | pair.second.clear(); 202 | } 203 | 204 | mGroupsForIndex.clear(); 205 | } 206 | 207 | void Pool::ResetCreationIndex() 208 | { 209 | mCreationIndex = 0; 210 | } 211 | 212 | void Pool::ClearComponentPool(const ComponentId index) 213 | { 214 | while(! mComponentPools.at(index).empty()) 215 | { 216 | delete mComponentPools.at(index).top(); 217 | mComponentPools.at(index).pop(); 218 | } 219 | } 220 | 221 | void Pool::ClearComponentPools() 222 | { 223 | for(const auto &pair : mComponentPools) 224 | { 225 | ClearComponentPool(pair.first); 226 | } 227 | } 228 | 229 | void Pool::Reset() 230 | { 231 | ClearGroups(); 232 | DestroyAllEntities(); 233 | ResetCreationIndex(); 234 | } 235 | 236 | auto Pool::GetEntityCount() const -> unsigned int 237 | { 238 | return mEntities.size(); 239 | } 240 | 241 | auto Pool::GetReusableEntitiesCount() const -> unsigned int 242 | { 243 | return mReusableEntities.size(); 244 | } 245 | 246 | auto Pool::GetRetainedEntitiesCount() const -> unsigned int 247 | { 248 | return mRetainedEntities.size(); 249 | } 250 | 251 | auto Pool::CreateSystem(std::shared_ptr system) -> std::shared_ptr 252 | { 253 | if(std::dynamic_pointer_cast(system) != nullptr) 254 | { 255 | (std::dynamic_pointer_cast(system)->SetPool(this)); 256 | } 257 | 258 | if(std::dynamic_pointer_cast(system) != nullptr) 259 | { 260 | return std::shared_ptr(new ReactiveSystem(this, std::dynamic_pointer_cast(system))); 261 | } 262 | 263 | if(std::dynamic_pointer_cast(system) != nullptr) 264 | { 265 | return std::shared_ptr(new ReactiveSystem(this, std::dynamic_pointer_cast(system))); 266 | } 267 | 268 | return system; 269 | } 270 | 271 | void Pool::UpdateGroupsComponentAddedOrRemoved(EntityPtr entity, ComponentId index, IComponent* component) 272 | { 273 | if(mGroupsForIndex.find(index) == mGroupsForIndex.end()) 274 | { 275 | return; 276 | } 277 | 278 | auto groups = mGroupsForIndex[index]; 279 | 280 | if (groups.size() > 0) 281 | { 282 | auto events = std::vector, Group::GroupChanged*>>(); 283 | 284 | for (int i = 0, groupsCount = groups.size(); i < groupsCount; ++i) 285 | { 286 | events.push_back(std::make_pair(groups[i], groups[i].lock()->HandleEntity(entity))); 287 | } 288 | 289 | for (const auto &pair : events) 290 | { 291 | if (pair.second != nullptr) 292 | { 293 | (*pair.second)(pair.first.lock(), entity, index, component); 294 | } 295 | } 296 | } 297 | } 298 | 299 | void Pool::UpdateGroupsComponentReplaced(EntityPtr entity, ComponentId index, IComponent* previousComponent, IComponent* newComponent) 300 | { 301 | if(mGroupsForIndex.find(index) == mGroupsForIndex.end()) 302 | { 303 | return; 304 | } 305 | 306 | if (mGroupsForIndex[index].size() > 0) 307 | { 308 | for(const auto &g : mGroupsForIndex[index]) 309 | { 310 | g.lock()->UpdateEntity(entity, index, previousComponent, newComponent); 311 | } 312 | } 313 | } 314 | 315 | void Pool::OnEntityReleased(Entity* entity) 316 | { 317 | if (entity->mIsEnabled) 318 | { 319 | throw std::runtime_error("Error, cannot release entity. Entity is not destroyed yet."); 320 | } 321 | 322 | mRetainedEntities.erase(entity); 323 | mReusableEntities.push(entity); 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /EntitasPP/Pool.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "Entity.hpp" 8 | #include "Group.hpp" 9 | #include 10 | #include 11 | 12 | namespace EntitasPP 13 | { 14 | class ISystem; 15 | 16 | class Pool 17 | { 18 | public: 19 | Pool(const unsigned int startCreationIndex = 1); 20 | ~Pool(); 21 | 22 | auto CreateEntity() -> EntityPtr; 23 | bool HasEntity(const EntityPtr& entity) const; 24 | void DestroyEntity(EntityPtr entity); 25 | void DestroyAllEntities(); 26 | 27 | auto GetEntities() -> std::vector; 28 | auto GetEntities(const Matcher matcher) -> std::vector; 29 | auto GetGroup(Matcher matcher) -> std::shared_ptr; 30 | 31 | void ClearGroups(); 32 | void ResetCreationIndex(); 33 | void ClearComponentPool(const ComponentId index); 34 | void ClearComponentPools(); 35 | void Reset(); 36 | 37 | auto GetEntityCount() const -> unsigned int; 38 | auto GetReusableEntitiesCount() const -> unsigned int; 39 | auto GetRetainedEntitiesCount() const -> unsigned int; 40 | 41 | auto CreateSystem(std::shared_ptr system) -> std::shared_ptr; 42 | template inline auto CreateSystem() -> std::shared_ptr; 43 | 44 | using PoolChanged = Delegate; 45 | using GroupChanged = Delegate group)>; 46 | 47 | PoolChanged OnEntityCreated; 48 | PoolChanged OnEntityWillBeDestroyed; 49 | PoolChanged OnEntityDestroyed; 50 | GroupChanged OnGroupCreated; 51 | GroupChanged OnGroupCleared; 52 | 53 | private: 54 | void UpdateGroupsComponentAddedOrRemoved(EntityPtr entity, ComponentId index, IComponent* component); 55 | void UpdateGroupsComponentReplaced(EntityPtr entity, ComponentId index, IComponent* previousComponent, IComponent* newComponent); 56 | void OnEntityReleased(Entity* entity); 57 | 58 | unsigned int mCreationIndex; 59 | std::unordered_set mEntities; 60 | std::unordered_map> mGroups; 61 | std::stack mReusableEntities; 62 | std::unordered_set mRetainedEntities; 63 | 64 | std::map> mComponentPools; 65 | std::map>> mGroupsForIndex; 66 | 67 | std::vector mEntitiesCache; 68 | std::function mOnEntityReleasedCache; 69 | std::function mOnComponentAddedCache; 70 | std::function mOnComponentRemovedCache; 71 | std::function mOnComponentReplacedCache; 72 | }; 73 | 74 | template 75 | auto Pool::CreateSystem() -> std::shared_ptr 76 | { 77 | return CreateSystem(std::dynamic_pointer_cast(std::shared_ptr(new T()))); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /EntitasPP/ReactiveSystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "ReactiveSystem.hpp" 6 | #include "Pool.hpp" 7 | #include "TriggerOnEvent.hpp" 8 | 9 | namespace EntitasPP 10 | { 11 | ReactiveSystem::ReactiveSystem(Pool* pool, std::shared_ptr subsystem) : 12 | ReactiveSystem(pool, subsystem, std::vector(1, *subsystem->trigger)) 13 | { 14 | } 15 | 16 | ReactiveSystem::ReactiveSystem(Pool* pool, std::shared_ptr subsystem) : 17 | ReactiveSystem(pool, subsystem, subsystem->triggers) 18 | { 19 | } 20 | 21 | ReactiveSystem::ReactiveSystem(Pool* pool, std::shared_ptr subsystem, std::vector triggers) 22 | { 23 | mSubsystem = subsystem; 24 | 25 | if(std::dynamic_pointer_cast(subsystem) != nullptr) 26 | { 27 | mEnsureComponents = (std::dynamic_pointer_cast(subsystem))->ensureComponents; 28 | } 29 | 30 | if(std::dynamic_pointer_cast(subsystem) != nullptr) 31 | { 32 | mExcludeComponents = (std::dynamic_pointer_cast(subsystem))->excludeComponents; 33 | } 34 | 35 | if(std::dynamic_pointer_cast(subsystem) != nullptr) 36 | { 37 | mClearAfterExecute = true; 38 | } 39 | 40 | unsigned int triggersLength = triggers.size(); 41 | auto groups = std::vector>(triggersLength); 42 | auto eventTypes = std::vector(triggersLength); 43 | 44 | for(unsigned int i = 0; i < triggersLength; ++i) 45 | { 46 | auto trigger = triggers[i]; 47 | groups[i] = pool->GetGroup(trigger.trigger); 48 | eventTypes[i] = trigger.eventType; 49 | } 50 | 51 | mObserver = new GroupObserver(groups, eventTypes); 52 | } 53 | 54 | ReactiveSystem::~ReactiveSystem () 55 | { 56 | Deactivate(); 57 | delete mObserver; 58 | } 59 | 60 | auto ReactiveSystem::GetSubsystem() const -> std::shared_ptr 61 | { 62 | return mSubsystem; 63 | } 64 | 65 | void ReactiveSystem::Activate() 66 | { 67 | mObserver->Activate(); 68 | } 69 | 70 | void ReactiveSystem::Deactivate() 71 | { 72 | mObserver->Deactivate(); 73 | } 74 | 75 | void ReactiveSystem::Clear() 76 | { 77 | mObserver->ClearCollectedEntities(); 78 | } 79 | 80 | void ReactiveSystem::Execute() 81 | { 82 | if(mObserver->GetCollectedEntities().size() != 0) 83 | { 84 | if(! mEnsureComponents.IsEmpty()) 85 | { 86 | if(! mExcludeComponents.IsEmpty()) 87 | { 88 | for(const auto &e : mObserver->GetCollectedEntities()) 89 | { 90 | if(mEnsureComponents.Matches(e) && ! mExcludeComponents.Matches(e)) 91 | { 92 | mEntityBuffer.push_back(e); 93 | } 94 | } 95 | } 96 | else 97 | { 98 | for(const auto &e : mObserver->GetCollectedEntities()) 99 | { 100 | if(mEnsureComponents.Matches(e)) 101 | { 102 | mEntityBuffer.push_back(e); 103 | } 104 | } 105 | } 106 | } 107 | else if(! mExcludeComponents.IsEmpty()) 108 | { 109 | for(const auto &e : mObserver->GetCollectedEntities()) 110 | { 111 | if(! mExcludeComponents.Matches(e)) 112 | { 113 | mEntityBuffer.push_back(e); 114 | } 115 | } 116 | } 117 | else 118 | { 119 | for(const auto &e : mObserver->GetCollectedEntities()) 120 | { 121 | mEntityBuffer.push_back(e); 122 | } 123 | } 124 | 125 | mObserver->ClearCollectedEntities(); 126 | 127 | if(mEntityBuffer.size() != 0) 128 | { 129 | mSubsystem->Execute(mEntityBuffer); 130 | mEntityBuffer.clear(); 131 | 132 | if(mClearAfterExecute) 133 | { 134 | mObserver->ClearCollectedEntities(); 135 | } 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /EntitasPP/ReactiveSystem.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "GroupObserver.hpp" 8 | #include "ISystem.hpp" 9 | 10 | namespace EntitasPP 11 | { 12 | class ReactiveSystem : public IExecuteSystem 13 | { 14 | public: 15 | ReactiveSystem(Pool* pool, std::shared_ptr subsystem); 16 | ReactiveSystem(Pool* pool, std::shared_ptr subsystem); 17 | ReactiveSystem(Pool* pool, std::shared_ptr subsystem, std::vector triggers); 18 | ~ReactiveSystem(); 19 | 20 | auto GetSubsystem() const -> std::shared_ptr; 21 | void Activate(); 22 | void Deactivate(); 23 | void Clear(); 24 | void Execute(); 25 | 26 | private: 27 | std::shared_ptr mSubsystem; 28 | GroupObserver* mObserver; 29 | Matcher mEnsureComponents; 30 | Matcher mExcludeComponents; 31 | bool mClearAfterExecute{false}; 32 | std::vector mEntityBuffer; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /EntitasPP/SystemContainer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "SystemContainer.hpp" 6 | #include "ReactiveSystem.hpp" 7 | #include 8 | 9 | namespace EntitasPP 10 | { 11 | auto SystemContainer::Add(std::shared_ptr system) -> SystemContainer* 12 | { 13 | if(std::dynamic_pointer_cast(system) != nullptr) 14 | { 15 | if(std::dynamic_pointer_cast((std::dynamic_pointer_cast(system))->GetSubsystem()) != nullptr) 16 | { 17 | mInitializeSystems.push_back(std::dynamic_pointer_cast((std::dynamic_pointer_cast(system))->GetSubsystem())); 18 | } 19 | } 20 | else 21 | { 22 | if(std::dynamic_pointer_cast(system) != nullptr) 23 | { 24 | mInitializeSystems.push_back(std::dynamic_pointer_cast(system)); 25 | } 26 | } 27 | 28 | if(std::dynamic_pointer_cast(system) != nullptr) 29 | { 30 | mExecuteSystems.push_back(std::dynamic_pointer_cast(system)); 31 | } 32 | 33 | if(std::dynamic_pointer_cast(system) != nullptr) 34 | { 35 | mFixedExecuteSystems.push_back(std::dynamic_pointer_cast(system)); 36 | } 37 | 38 | return this; 39 | } 40 | 41 | void SystemContainer::Initialize() 42 | { 43 | for(const auto &system : mInitializeSystems) 44 | { 45 | system->Initialize(); 46 | } 47 | } 48 | 49 | void SystemContainer::Execute() 50 | { 51 | for(const auto &system : mExecuteSystems) 52 | { 53 | system->Execute(); 54 | } 55 | } 56 | 57 | void SystemContainer::FixedExecute() 58 | { 59 | for(const auto &system : mFixedExecuteSystems) 60 | { 61 | system->FixedExecute(); 62 | } 63 | } 64 | 65 | void SystemContainer::ActivateReactiveSystems() 66 | { 67 | for(const auto &system : mExecuteSystems) 68 | { 69 | if(std::dynamic_pointer_cast(system) != nullptr) 70 | { 71 | (std::dynamic_pointer_cast(system))->Activate(); 72 | } 73 | 74 | if(std::dynamic_pointer_cast(system) != nullptr) 75 | { 76 | (std::dynamic_pointer_cast(system))->ActivateReactiveSystems(); 77 | } 78 | } 79 | } 80 | 81 | void SystemContainer::DeactivateReactiveSystems() 82 | { 83 | for(const auto &system : mExecuteSystems) 84 | { 85 | if(std::dynamic_pointer_cast(system) != nullptr) 86 | { 87 | (std::dynamic_pointer_cast(system))->Deactivate(); 88 | } 89 | 90 | if(std::dynamic_pointer_cast(system) != nullptr) 91 | { 92 | (std::dynamic_pointer_cast(system))->DeactivateReactiveSystems(); 93 | } 94 | } 95 | } 96 | 97 | void SystemContainer::ClearReactiveSystems() 98 | { 99 | for(const auto &system : mExecuteSystems) 100 | { 101 | if(std::dynamic_pointer_cast(system) != nullptr) 102 | { 103 | (std::dynamic_pointer_cast(system))->Clear(); 104 | } 105 | 106 | if(std::dynamic_pointer_cast(system) != nullptr) 107 | { 108 | (std::dynamic_pointer_cast(system))->ClearReactiveSystems(); 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /EntitasPP/SystemContainer.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "ISystem.hpp" 8 | #include 9 | 10 | namespace EntitasPP 11 | { 12 | class SystemContainer : public IInitializeSystem, public IExecuteSystem, public IFixedExecuteSystem 13 | { 14 | public: 15 | SystemContainer() = default; 16 | 17 | auto Add(std::shared_ptr system) -> SystemContainer*; 18 | template inline auto Add() -> SystemContainer*; 19 | 20 | void Initialize(); 21 | void Execute(); 22 | void FixedExecute(); 23 | 24 | void ActivateReactiveSystems(); 25 | void DeactivateReactiveSystems(); 26 | void ClearReactiveSystems(); 27 | 28 | private: 29 | std::vector> mInitializeSystems; 30 | std::vector> mExecuteSystems; 31 | std::vector> mFixedExecuteSystems; 32 | }; 33 | 34 | template 35 | auto SystemContainer::Add() -> SystemContainer* 36 | { 37 | return Add(std::shared_ptr(new T())); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /EntitasPP/TriggerOnEvent.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #pragma once 6 | 7 | #include "Matcher.hpp" 8 | #include "GroupEventType.hpp" 9 | 10 | namespace EntitasPP 11 | { 12 | struct TriggerOnEvent 13 | { 14 | public: 15 | TriggerOnEvent(const Matcher trigger, const GroupEventType eventType) 16 | { 17 | this->trigger = trigger; 18 | this->eventType = eventType; 19 | } 20 | 21 | Matcher trigger; 22 | GroupEventType eventType; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright © 2016 Juan Delgado (JuDelCo) 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Entitas++ 2 | ===================== 3 | 4 | Entitas++ is a fast Entity Component System Framework (ECS) C++11 port of [Entitas v0.29.0 for C# and Unity](https://github.com/sschmid/Entitas-CSharp/tree/0.29.0). 5 | 6 | There are some differences between both, so please check the code and notes below ! 7 | 8 | Any feedback is welcome ! 9 | 10 | Documentation 11 | ===================== 12 | 13 | #### Overview 14 | 15 | As in Entitas C#, basic code architecture is the same: 16 | 17 | ``` 18 | +------------------+ 19 | | Pool | 20 | |------------------| 21 | | e e | +-----------+ 22 | | e e---|----> | Entity | 23 | | e e | |-----------| 24 | | e e e | | Component | 25 | | e e | | | +-----------+ 26 | | e e | | Component-|----> | Component | 27 | | e e e | | | |-----------| 28 | | e e e | | Component | | Data | 29 | +------------------+ +-----------+ +-----------+ 30 | | 31 | | 32 | | +-------------+ Groups: 33 | | | e | Subsets of entities in the pool 34 | | | e e | for blazing fast querying 35 | +---> | +------------+ 36 | | e | | | 37 | | e | e | e | 38 | +--------|----+ e | 39 | | e | 40 | | e e | 41 | +------------+ 42 | ``` 43 | 44 | #### Creating a new component example 45 | 46 | ###### Entitas C# 47 | ```csharp 48 | public class PositionComponent : IComponent { 49 | public float x; 50 | public float y; 51 | public float z; 52 | } 53 | 54 | //... 55 | 56 | var pool = new Pool(ComponentIds.TotalComponents); 57 | var e = pool.CreateEntity(); 58 | 59 | e.AddPosition(1f, 2f, 3f); 60 | e.ReplacePosition(3f, 2f, 1f); 61 | e.RemovePosition(); 62 | 63 | var posX = e.position.x; 64 | var hasPosition = e.hasPosition; 65 | ``` 66 | 67 | ###### Entitas++ 68 | ```cpp 69 | // I suggest you to use structs instead, they are the same and all members/methods are public by default. 70 | class Position : public IComponent { 71 | public: 72 | // You must provide at least ONE public "Reset" method with any parameters you want 73 | void Reset(float px, float py, float pz) { 74 | x = px; 75 | y = py; 76 | z = pz; 77 | } 78 | 79 | float x; 80 | float y; 81 | float z; 82 | }; 83 | 84 | //... 85 | 86 | auto pool = new Pool(); 87 | auto e = pool->CreateEntity(); 88 | 89 | e->Add(1f, 2f, 3f); 90 | e->Replace(3f, 2f, 1f); 91 | e->Remove(); 92 | 93 | float posX = e->Get()->x; 94 | bool hasPosition = e->Has(); 95 | ``` 96 | 97 | #### Basic component management 98 | 99 | ###### Entitas C# 100 | ```csharp 101 | entity.AddPosition(3, 7); 102 | entity.AddHealth(100); 103 | entity.isMovable = true; 104 | 105 | entity.ReplacePosition(10, 100); 106 | entity.ReplaceHealth(entity.health.value - 1); 107 | entity.isMovable = false; 108 | 109 | entity.RemovePosition(); 110 | 111 | var hasPos = entity.hasPosition; 112 | var movable = entity.isMovable; 113 | ``` 114 | 115 | ###### Entitas++ 116 | ```cpp 117 | entity->Add(3, 7); 118 | entity->Add(100); 119 | entity->Replace(); // There is no helper in here, you must use "Replace" and "Remove" methods 120 | 121 | entity->Replace(10, 100); 122 | entity->Replace(entity->Get()->value - 1); 123 | entity->Remove(); 124 | 125 | entity->Remove(); 126 | 127 | bool hasPos = entity->Has(); 128 | bool movable = entity->Has(); // Again, you must use "Has" to check if an entity has a component 129 | ``` 130 | 131 | #### Pools 132 | 133 | ###### Entitas C# 134 | ```csharp 135 | // Pools.pool is kindly generated for you by the code generator 136 | var pool = Pools.pool; 137 | var entity = pool.CreateEntity(); 138 | entity.isMovable = true; 139 | 140 | // Returns all entities having MovableComponent and PositionComponent. 141 | // Matchers are also generated for you. 142 | var entities = pool.GetEntities(Matcher.AllOf(Matcher.Movable, Matcher.Position)); 143 | foreach (var e in entities) { 144 | // do something 145 | } 146 | ``` 147 | 148 | ###### Entitas++ 149 | ```cpp 150 | // No code generator == no "Pools". You must handle yourself your Pools ! 151 | auto pool = new Pool(); 152 | auto entity = pool->CreateEntity(); 153 | entity->Replace(); // I used "Replace" but in this case is better to use "Add" directly 154 | 155 | // Returns all entities having MovableComponent and PositionComponent. 156 | // Matchers are also generated for you. 157 | auto entities = pool->GetEntities(Matcher_AllOf(Movable, Position)); // *Some magic preprocessor involved* 158 | for (auto &e : entities) { // e is a shared_ptr of Entity 159 | // do something 160 | } 161 | ``` 162 | 163 | #### Group events 164 | 165 | ###### Entitas C# 166 | ```csharp 167 | pool.GetGroup(Matcher.Position).OnEntityAdded += (group, entity, index, component) => { 168 | // Do something 169 | }; 170 | ``` 171 | 172 | ###### Entitas++ 173 | ```cpp 174 | pool->GetGroup(Matcher_AllOf(Position))->OnEntityAdded += [](std::shared_ptr group, EntityPtr entity, ComponentId index, IComponent* component) { 175 | // Do something 176 | }; 177 | ``` 178 | 179 | #### Group Observer 180 | 181 | ###### Entitas C# 182 | ```csharp 183 | var group = pool.GetGroup(Matcher.Position); 184 | var observer = group.CreateObserver(GroupEventType.OnEntityAdded); 185 | foreach (var e in observer.collectedEntities) { 186 | // do something 187 | } 188 | observer.ClearCollectedEntities(); 189 | ``` 190 | 191 | ###### Entitas++ 192 | ```cpp 193 | auto group = pool->GetGroup(Matcher_AllOf(Position)); // There are _AnyOf and _NoneOf too ! 194 | auto observer = group->CreateObserver(GroupEventType::OnEntityAdded); 195 | for (auto &e : observer->GetCollectedEntities()) { 196 | // do something 197 | } 198 | observer->ClearCollectedEntities(); 199 | ``` 200 | 201 | #### Execute Systems 202 | 203 | ###### Entitas C# 204 | ```csharp 205 | public class MoveSystem : IExecuteSystem, ISetPool { 206 | Group _group; 207 | 208 | public void SetPool(Pool pool) { 209 | _group = pool.GetGroup(Matcher.AllOf(Matcher.Move, Matcher.Position)); 210 | } 211 | 212 | public void Execute() { 213 | foreach (var e in _group.GetEntities()) { 214 | var move = e.move; 215 | var pos = e.position; 216 | e.ReplacePosition(pos.x, pos.y + move.speed, pos.z); 217 | } 218 | } 219 | } 220 | ``` 221 | 222 | ###### Entitas++ 223 | ```cpp 224 | class MoveSystem : public IExecuteSystem, public ISetPool { 225 | std::weak_ptr _group; 226 | 227 | public: 228 | void SetPool(Pool* pool) { 229 | _group = pool->GetGroup(Matcher_AllOf(Move, Position)); 230 | } 231 | 232 | void Execute() { 233 | for (auto &e : _group.lock()->GetEntities()) { 234 | auto move = e->Get(); 235 | auto pos = e->Get(); 236 | e->Replace(pos->x, pos->y + move->speed, pos->z); 237 | } 238 | } 239 | }; 240 | ``` 241 | 242 | #### Reactive Systems 243 | 244 | ###### Entitas C# 245 | ```csharp 246 | public class RenderPositionSystem : IReactiveSystem { 247 | public TriggerOnEvent trigger { get { return Matcher.AllOf(Matcher.Position, Matcher.View).OnEntityAdded(); } } 248 | 249 | public void Execute(List entities) { 250 | // Gets executed only if the observed group changed. 251 | // Changed entities are passed as an argument 252 | foreach (var e in entities) { 253 | var pos = e.position; 254 | e.view.gameObject.transform.position = new Vector3(pos.x, pos.y, pos.z); 255 | } 256 | } 257 | } 258 | ``` 259 | 260 | ###### Entitas++ 261 | ```cpp 262 | class RenderPositionSystem : public IReactiveSystem { 263 | public: 264 | RenderPositionSystem() { 265 | trigger = Matcher_AllOf(Position, View).OnEntityAdded(); 266 | } 267 | 268 | void Execute(std::vector entities) { 269 | // Gets executed only if the observed group changed. 270 | // Changed entities are passed as an argument 271 | for (auto &e : entities) { 272 | auto pos = e->Get(); 273 | // NOTE: Unity-only example, but this maybe could be the code if Unity were compatible with C++ 274 | e->Get()->gameObject.transform.position = new Vector3(pos->x, pos->y, pos->z); 275 | } 276 | } 277 | } 278 | ``` 279 | 280 | Notes 281 | ===================== 282 | 283 | - All code above was written using: ```using namespace EntitasPP;```, if you don't, you must prepend all the classes with the namespace ```EntitasPP::```. 284 | - There is *no* code generator because C++ lacks of [code reflection](https://en.wikipedia.org/wiki/Reflection_(computer_programming)). So all code must be done by you, but there are a lot of templating involved in here to ease the work for you anyways (see code above). 285 | - ['Systems' class](https://github.com/sschmid/Entitas-CSharp/blob/94ab9b172987a65a7facfd4c383b621a4cbb0bca/Entitas/Entitas/Systems.cs#L8) was renamed to 'SystemContainer'. You can see a simple example of use in the provided 'main.cpp'. 286 | - All 'ToString()' methods were removed. If you need to track identifiers between entities I suggest you to use your own custom Component. 287 | - AERC (Automatic Entity Reference Counting) was implemented using smart pointers. (Yeah! Take that C# :stuck_out_tongue_winking_eye:) 288 | 289 | If you need more documentation of the architecture of the framework, please go to [Entitas v0.29.0 C# Wiki](https://github.com/sschmid/Entitas-CSharp/wiki/Home/18f70d987d94be972c179bbd71b3a175263c0f04) since this framework has a lot on common with the original C# one. 290 | 291 | The MIT License (MIT) 292 | ===================== 293 | 294 | Copyright © 2020 Juan Delgado (JuDelCo) 295 | 296 | Permission is hereby granted, free of charge, to any person 297 | obtaining a copy of this software and associated documentation 298 | files (the “Software”), to deal in the Software without 299 | restriction, including without limitation the rights to use, 300 | copy, modify, merge, publish, distribute, sublicense, and/or sell 301 | copies of the Software, and to permit persons to whom the 302 | Software is furnished to do so, subject to the following 303 | conditions: 304 | 305 | The above copyright notice and this permission notice shall be 306 | included in all copies or substantial portions of the Software. 307 | 308 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 309 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 310 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 311 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 312 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 313 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 314 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 315 | OTHER DEALINGS IN THE SOFTWARE. 316 | -------------------------------------------------------------------------------- /clang_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Build started..." 4 | 5 | start=`date +%s` 6 | clang++ -std=c++11 -stdlib=libc++ -g \ 7 | EntitasPP/ComponentTypeId.cpp \ 8 | EntitasPP/Entity.cpp \ 9 | EntitasPP/Group.cpp \ 10 | EntitasPP/GroupObserver.cpp \ 11 | EntitasPP/Matcher.cpp \ 12 | EntitasPP/Pool.cpp \ 13 | EntitasPP/ReactiveSystem.cpp \ 14 | EntitasPP/SystemContainer.cpp \ 15 | main.cpp \ 16 | -o bin/main-build 17 | end=`date +%s` 18 | run_time=$((end-start)) 19 | 20 | echo "Build finished ($run_times)" 21 | 22 | read -s -n 1 -p "Press any key to close ..." 23 | echo "" 24 | 25 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Juan Delgado (JuDelCo) 2 | // License: MIT License 3 | // MIT License web page: https://opensource.org/licenses/MIT 4 | 5 | #include "EntitasPP/SystemContainer.hpp" 6 | #include "EntitasPP/Matcher.hpp" 7 | #include "EntitasPP/Pool.hpp" 8 | #include 9 | #include 10 | 11 | using namespace EntitasPP; 12 | 13 | class DemoComponent : public IComponent { 14 | public: 15 | void Reset(const std::string& name1, const std::string& name2) { 16 | std::cout << "Created new entity: " << name1 << "," << name2 << std::endl; 17 | } 18 | }; 19 | 20 | class DemoSystem : public IInitializeSystem, public IExecuteSystem, public ISetPoolSystem { 21 | public: 22 | void SetPool(Pool* pool) { 23 | mPool = pool; 24 | } 25 | void Initialize() { 26 | mPool->CreateEntity()->Add("foo", "bar"); 27 | std::cout << "DemoSystem initialized" << std::endl; 28 | } 29 | void Execute() { 30 | mPool->CreateEntity()->Add("foo", "bar"); 31 | 32 | auto entitiesCount = mPool->GetGroup(Matcher_AllOf(DemoComponent))->Count(); 33 | std::cout << "There are " << entitiesCount << " entities with the component 'DemoComponent'" << std::endl; 34 | 35 | std::cout << "DemoSystem executed" << std::endl; 36 | } 37 | 38 | private: 39 | Pool* mPool; 40 | }; 41 | 42 | int main(const int argc, const char* argv[]) { 43 | auto systems = std::make_shared(); 44 | auto pool = std::make_shared(); 45 | 46 | systems->Add(pool->CreateSystem()); 47 | systems->Initialize(); 48 | 49 | for(unsigned int i = 0; i < 2; ++i) { 50 | systems->Execute(); 51 | } 52 | 53 | return 0; 54 | } 55 | --------------------------------------------------------------------------------