diff options
Diffstat (limited to 'src/entity_manager.h')
-rw-r--r-- | src/entity_manager.h | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/entity_manager.h b/src/entity_manager.h new file mode 100644 index 0000000..7fd82fc --- /dev/null +++ b/src/entity_manager.h | |||
@@ -0,0 +1,211 @@ | |||
1 | #ifndef ENTITY_MANAGER_H_C5832F11 | ||
2 | #define ENTITY_MANAGER_H_C5832F11 | ||
3 | |||
4 | #include <map> | ||
5 | #include <vector> | ||
6 | #include <typeindex> | ||
7 | #include <set> | ||
8 | #include <stdexcept> | ||
9 | #include "component.h" | ||
10 | #include "algorithms.h" | ||
11 | |||
12 | class EntityManager { | ||
13 | private: | ||
14 | |||
15 | struct EntityData { | ||
16 | std::map<std::type_index, std::unique_ptr<Component>> components; | ||
17 | }; | ||
18 | |||
19 | using database_type = std::vector<EntityData>; | ||
20 | |||
21 | public: | ||
22 | |||
23 | using id_type = database_type::size_type; | ||
24 | |||
25 | private: | ||
26 | |||
27 | database_type entities; | ||
28 | std::vector<bool> slotAvailable; | ||
29 | std::map<std::set<std::type_index>, std::set<id_type>> cachedComponents; | ||
30 | |||
31 | id_type nextEntityID = 0; | ||
32 | |||
33 | template <class T, class... R> | ||
34 | std::set<id_type> getEntitiesWithComponentsHelper( | ||
35 | std::set<std::type_index>& componentTypes) | ||
36 | { | ||
37 | componentTypes.insert(typeid(T)); | ||
38 | |||
39 | return getEntitiesWithComponents<R...>(componentTypes); | ||
40 | } | ||
41 | |||
42 | template <class... R> | ||
43 | std::set<id_type> getEntitiesWithComponents( | ||
44 | std::set<std::type_index>& componentTypes) | ||
45 | { | ||
46 | return getEntitiesWithComponentsHelper<R...>(componentTypes); | ||
47 | } | ||
48 | |||
49 | public: | ||
50 | |||
51 | EntityManager() = default; | ||
52 | |||
53 | EntityManager(const EntityManager& copy) = delete; | ||
54 | |||
55 | id_type emplaceEntity() | ||
56 | { | ||
57 | if (nextEntityID >= entities.size()) | ||
58 | { | ||
59 | // If the database is saturated, add a new element for the new entity. | ||
60 | entities.emplace_back(); | ||
61 | slotAvailable.push_back(false); | ||
62 | |||
63 | return nextEntityID++; | ||
64 | } else { | ||
65 | // If there is an available slot in the database, use it. | ||
66 | id_type id = nextEntityID++; | ||
67 | slotAvailable[id] = false; | ||
68 | |||
69 | // Fast forward the next available slot pointer to an available slot. | ||
70 | while ((nextEntityID < entities.size()) && !slotAvailable[nextEntityID]) | ||
71 | { | ||
72 | nextEntityID++; | ||
73 | } | ||
74 | |||
75 | return id; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | void deleteEntity(id_type entity) | ||
80 | { | ||
81 | if ((entity >= entities.size()) || slotAvailable[entity]) | ||
82 | { | ||
83 | throw std::invalid_argument("Cannot delete non-existent entity"); | ||
84 | } | ||
85 | |||
86 | // Uncache components | ||
87 | for (auto& cache : cachedComponents) | ||
88 | { | ||
89 | cache.second.erase(entity); | ||
90 | } | ||
91 | |||
92 | // Destroy the data | ||
93 | entities[entity].components.clear(); | ||
94 | |||
95 | // Mark the slot as available | ||
96 | slotAvailable[entity] = true; | ||
97 | |||
98 | if (entity < nextEntityID) | ||
99 | { | ||
100 | nextEntityID = entity; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | template <class T, class... Args> | ||
105 | T& emplaceComponent(id_type entity, Args&&... args) | ||
106 | { | ||
107 | if ((entity >= entities.size()) || slotAvailable[entity]) | ||
108 | { | ||
109 | throw std::invalid_argument("Cannot get non-existent entity"); | ||
110 | } | ||
111 | |||
112 | EntityData& data = entities[entity]; | ||
113 | std::type_index componentType = typeid(T); | ||
114 | |||
115 | if (data.components.count(componentType)) | ||
116 | { | ||
117 | throw std::invalid_argument("Cannot emplace already-existent component"); | ||
118 | } | ||
119 | |||
120 | // Initialize the component | ||
121 | std::unique_ptr<T> ptr(new T(std::forward<Args>(args)...)); | ||
122 | T& component = *ptr; | ||
123 | data.components[componentType] = std::move(ptr); | ||
124 | |||
125 | // Invalidate related caches | ||
126 | erase_if( | ||
127 | cachedComponents, | ||
128 | [&componentType] ( | ||
129 | std::pair<const std::set<std::type_index>, std::set<id_type>>& cache) { | ||
130 | return cache.first.count(componentType) == 1; | ||
131 | }); | ||
132 | |||
133 | return component; | ||
134 | } | ||
135 | |||
136 | template <class T> | ||
137 | void removeComponent(id_type entity) | ||
138 | { | ||
139 | if ((entity >= entities.size()) || slotAvailable[entity]) | ||
140 | { | ||
141 | throw std::invalid_argument("Cannot get non-existent entity"); | ||
142 | } | ||
143 | |||
144 | EntityData& data = entities[entity]; | ||
145 | std::type_index componentType = typeid(T); | ||
146 | |||
147 | if (!data.components.count(componentType)) | ||
148 | { | ||
149 | throw std::invalid_argument("Cannot delete non-existent component"); | ||
150 | } | ||
151 | |||
152 | // Destroy the component | ||
153 | data.components.erase(componentType); | ||
154 | |||
155 | // Uncache the component | ||
156 | for (auto& cache : cachedComponents) | ||
157 | { | ||
158 | if (cache.first.count(componentType) == 1) | ||
159 | { | ||
160 | cache.second.erase(entity); | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | template <class T> | ||
166 | T& getComponent(id_type entity) | ||
167 | { | ||
168 | if ((entity >= entities.size()) || slotAvailable[entity]) | ||
169 | { | ||
170 | throw std::invalid_argument("Cannot get non-existent entity"); | ||
171 | } | ||
172 | |||
173 | EntityData& data = entities[entity]; | ||
174 | std::type_index componentType = typeid(T); | ||
175 | |||
176 | if (!data.components.count(componentType)) | ||
177 | { | ||
178 | throw std::invalid_argument("Cannot get non-existent component"); | ||
179 | } | ||
180 | |||
181 | return *dynamic_cast<T*>(data.components[componentType].get()); | ||
182 | } | ||
183 | |||
184 | template <class T> | ||
185 | bool hasComponent(id_type entity) | ||
186 | { | ||
187 | if ((entity >= entities.size()) || slotAvailable[entity]) | ||
188 | { | ||
189 | throw std::invalid_argument("Cannot get non-existent entity"); | ||
190 | } | ||
191 | |||
192 | EntityData& data = entities[entity]; | ||
193 | std::type_index componentType = typeid(T); | ||
194 | |||
195 | return data.components.count(componentType); | ||
196 | } | ||
197 | |||
198 | template <class... R> | ||
199 | std::set<id_type> getEntitiesWithComponents() | ||
200 | { | ||
201 | std::set<std::type_index> componentTypes; | ||
202 | |||
203 | return getEntitiesWithComponentsHelper<R...>(componentTypes); | ||
204 | } | ||
205 | }; | ||
206 | |||
207 | template <> | ||
208 | std::set<EntityManager::id_type> EntityManager::getEntitiesWithComponents<>( | ||
209 | std::set<std::type_index>& componentTypes); | ||
210 | |||
211 | #endif /* end of include guard: ENTITY_MANAGER_H_C5832F11 */ | ||