jubilant-funicular
ECS.h
1 #ifndef NTA_ECS_H_INCLUDED
2 #define NTA_ECS_H_INCLUDED
3 
4 #include <unordered_set>
5 #include <unordered_map>
6 #include <vector>
7 
8 #include "nta/Option.h"
9 #include "nta/IDFactory.h"
10 #include "nta/TypeMap.h"
11 #include "nta/SlotMap.h"
12 #include "nta/Event.h"
13 
14 namespace nta {
15  class ECS;
16 
17  using Entity = utils::SlotMapKey<>;
18  using ComponentID = utils::SlotMapKey<>;
19  template<typename T>
20  using ComponentList = utils::SlotMap<T>;
21 
23  class Component {
24  protected:
26  Component() {}
29  public:
30  virtual ~Component() {}
32  const ComponentID get_id() const { return m_id; }
33 
34  friend ECS;
35  };
38  public:
39  struct Record {
40  private:
41  Record() {}
42  public:
43  template<typename T>
44  static const Record get() {
45  static_assert(std::is_base_of_v<nta::Component, T>,
46  "ComponentRegistry: Can only register types deriving from nta::Component");
47  Record ret;
48 
49  ret.create_list = [](utils::TypeMap& map) {
50  map.insert<ComponentList<T>>(ComponentList<T>());
51  };
52  ret.delete_entity = [](const utils::TypeMap& map, utils::IDFactory<ComponentID>& cmpn_gen, Entity e) {
53  if (!map.contains<ComponentList<T>>()) return;
54  auto& list = map.find<ComponentList<T>>();
55  list[e].map([&](T& cmpn) {
56  cmpn_gen.free(cmpn.get_id());
57  });
58  list.remove(e);
59  };
60  ret.delete_component = [](const utils::TypeMap& map, ComponentID c, Entity e) -> bool {
61  if (!map.contains<ComponentList<T>>()) return false;
62  auto& list = map.find<ComponentList<T>>();
63  list.deactivate(e);
64  return true;
65  };
66  ret.get_component = [](const utils::TypeMap& map, Entity e) -> utils::Option<Component&> {
67  if (!map.contains<ComponentList<T>>()) return utils::Option<Component&>::none();
68  auto& list = map.find<ComponentList<T>>();
69  return list[e];
70  };
71  ret.num_components = [](const utils::TypeMap& map) -> std::size_t {
72  if (!map.contains<ComponentList<T>>()) return 0;
73  return map.find<ComponentList<T>>().size();
74  };
75  ret.clear = [](const utils::TypeMap& map) {
76  if (!map.contains<ComponentList<T>>()) return;
77  map.find<ComponentList<T>>().clear();
78  };
79  return ret;
80  }
81 
82  std::function<void(utils::TypeMap&)> create_list;
83  std::function<void(const utils::TypeMap&, utils::IDFactory<ComponentID>&, Entity)> delete_entity;
84  std::function<bool(const utils::TypeMap&, ComponentID, Entity)> delete_component;
85  std::function<utils::Option<Component&>(const utils::TypeMap&, Entity)> get_component;
86  std::function<std::size_t(const utils::TypeMap&)> num_components;
87  std::function<void(const utils::TypeMap&)> clear;
88  };
89 
90  using iterator = std::unordered_map<std::size_t, Record>::iterator;
91  using const_iterator = std::unordered_map<std::size_t, Record>::const_iterator;
92  private:
93  std::unordered_map<std::size_t, Record> m_records;
94  public:
96  ~ComponentRegistry() {}
97 
98  template<typename T>
99  void register_component() { m_records.insert(std::make_pair(typeid(T).hash_code(), Record::get<T>())); }
100 
101  template<typename T>
102  utils::Option<Record> get_record() const { return operator[](typeid(T).hash_code()); }
103  utils::Option<Record> operator[](std::size_t hash) const {
104  auto rec = m_records.find(hash);
105  return rec == m_records.end() ? utils::Option<Record>::none() : utils::Option<Record>::some(rec->second);
106  };
107 
108  std::size_t size() const { return m_records.size(); }
109 
110  iterator begin() { return m_records.begin(); }
111  const_iterator begin() const { return m_records.cbegin(); }
112  const_iterator cbegin() const { return m_records.cbegin(); }
113  iterator end() { return m_records.end(); }
114  const_iterator end() const { return m_records.cend(); }
115  const_iterator cend() const { return m_records.cend(); }
116  };
134  // I really should have just called this thing EntityManager
135  class ECS {
136  private:
138  struct ComponentInfo {
139  std::size_t type;
140  Entity owner;
141  };
142 
162  public:
163  ECS(const ComponentRegistry& registry);
164  ~ECS() { clear(); }
165 
167  Entity gen_entity();
169  void gen_entities(std::size_t num, Entity* ids);
173  bool delete_entity(Entity id);
174  bool delete_owner(ComponentID cmpn);
176  std::size_t num_entities() const { return m_entity_gen.get_count(); }
178  template<typename T>
179  std::size_t num_components() const { return get_component_list<T>().size(); }
180  std::size_t num_components() const;
181 
189  template<typename T, typename... Args>
190  utils::Option<ComponentID> add_component(Entity entity, Args&&... args);
191  template<typename T, typename... Args>
192  utils::Option<ComponentID> add_sibling(ComponentID cmpn, Args&&... args);
196  bool delete_component(ComponentID cmpn);
197  template<typename T>
198  bool delete_component(Entity entity);
199 
201  template<typename T>
202  bool has_component(Entity entity) const;
203  template<typename T>
204  bool has_sibling(ComponentID cmpn) const;
206  bool does_entity_exist(Entity entity) const;
207 
211  template<typename T>
214  template<typename T>
215  utils::Option<T&> get_component(Entity entity) const;
219  template<typename T>
221 
223  template<typename T>
224  void for_each(std::function<void(T&)> func) const;
225 
227  template<typename T, typename Event>
228  void enact_on(const Event& event, Entity entity) const;
229  template<typename T, typename Event, typename Event::enum_type e>
230  void enact_on(const Event& event, Entity entity) const;
231  template<typename T, typename Event>
232  void enact_on(const Event& event, typename Event::enum_type e, Entity entity) const;
234  template<typename T, typename Event>
235  void enact_on_sibling(const Event& event, ComponentID cmpn) const;
236  template<typename T, typename Event, typename Event::enum_type e>
237  void enact_on_sibling(const Event& event, ComponentID cmpn) const;
238  template<typename T, typename Event>
239  void enact_on_sibling(const Event& event, typename Event::enum_type e, ComponentID cmpn) const;
241  template<typename T, typename Event>
242  void enact_on_all(const Event& event, typename Event::enum_type e) const;
243  template<typename T, typename Event, typename Event::enum_type e>
244  void enact_on_all(const Event& event) const;
245 
247  void clear();
248  };
249  template<typename T, typename... Args>
251  if (!does_entity_exist(entity)) return utils::make_none<ComponentID>();
253  list.reserve(entity.idx+1);
254 
255  if (!list.insert_emplace(entity, std::forward<Args>(args)...)) {
256  return utils::make_none<ComponentID>();
257  }
258  T& cmpn = list[entity].unwrap();
259  cmpn.m_id = m_cmpn_gen();
260 
261  m_component_info.reserve(cmpn.m_id.idx+1);
262  ComponentInfo info = { .type = typeid(T).hash_code(), .owner = entity };
263  if (!m_component_info.insert(cmpn.m_id, info)) {
264  assert(false && "This should never happen");
265  }
266  return utils::make_some(cmpn.m_id);
267  }
268  template<typename T, typename... Args>
269  utils::Option<ComponentID> ECS::add_sibling(ComponentID cmpn, Args&&... args) {
270  return get_owner(cmpn).map<ComponentID>([&](Entity e) {
271  return add_component<T>(e, std::forward<Args>(args)...);
272  });
273  }
274  template<typename T>
275  bool ECS::delete_component(Entity entity) {
276  auto maybe_cmpn = get_component<T>(entity);
277  return maybe_cmpn ? delete_component(maybe_cmpn.unwrap().get_id()) : false;
278  }
279  template<typename T>
280  bool ECS::has_component(Entity entity) const {
281  if (!does_entity_exist(entity)) return false;
282  if (!m_components.contains<ComponentList<T>>()) return false;
283  return m_components.find<ComponentList<T>>()[entity].is_some();
284  }
285  template<typename T>
286  bool ECS::has_sibling(ComponentID cmpn) const {
287  auto maybe_owner = get_owner(cmpn);
288  return maybe_owner ? has_component<T>(maybe_owner.unwrap()) : false;
289  }
290  template<typename T>
292  if (!m_components.contains<ComponentList<T>>()) {
293  assert(false && "Attempted to get a ComponentList for a non-registered type");
294  }
296  }
297  template<typename T>
299  if (!has_component<T>(entity)) return utils::Option<T&>::none();
300  return m_components.find<ComponentList<T>>()[entity];
301  }
302  template<typename T>
304  return get_owner(cmpn).map<T&>([&](Entity e) { return get_component<T>(e); });
305  }
306  template<typename T>
307  void ECS::for_each(std::function<void(T&)> func) const {
308  auto& list = get_component_list<T>();
309  for (auto& cmpn : list) {
310  func(cmpn);
311  }
312  }
313  template<typename T, typename Event>
314  void ECS::enact_on(const Event& event, Entity entity) const {
315  get_component<T>(entity).map([&](T& cmpn) { event(cmpn); });
316  }
317  template<typename T, typename Event, typename Event::enum_type e>
318  void ECS::enact_on(const Event& event, Entity entity) const {
319  get_component<T>(entity).map([&](T& cmpn) { event.enact<e>(cmpn); });
320  }
321  template<typename T, typename Event>
322  void ECS::enact_on(const Event& event, typename Event::enum_type e, Entity entity) const {
323  get_component<T>(entity).map([&](T& cmpn) { event(e, cmpn); });
324  }
325  template<typename T, typename Event>
326  void ECS::enact_on_sibling(const Event& event, ComponentID cmpn) const {
327  get_sibling<T>(cmpn).map([&](T& sib) { event(sib); });
328  }
329  template<typename T, typename Event, typename Event::enum_type e>
330  void ECS::enact_on_sibling(const Event& event, ComponentID cmpn) const {
331  get_sibling<T>(cmpn).map([&](T& sib) { event.enact<e>(sib); });
332  }
333  template<typename T, typename Event>
334  void ECS::enact_on_sibling(const Event& event, typename Event::enum_type e, ComponentID cmpn) const {
335  get_sibling<T>(cmpn).map([&](T& sib) { event(e, sib); });
336  }
337  template<typename T, typename Event>
338  void ECS::enact_on_all(const Event& event, typename Event::enum_type e) const {
339  for (auto& cmpn : get_component_list<T>()) {
340  event(e, cmpn);
341  }
342  }
343  template<typename T, typename Event, typename Event::enum_type e>
344  void ECS::enact_on_all(const Event& event) const {
345  for (auto& cmpn : get_component_list<T>()) {
346  event.enact<e>(cmpn);
347  }
348  }
349 }
350 
351 #endif // NTA_ECS_H_INCLUDED
nta::utils::make_some
Option< T > make_some(const T &data)
Replacement for calling Option<T>::some()
Definition: Option.h:134
utils::TypeMap::find
std::add_lvalue_reference< T >::type find() const
Definition: TypeMap.h:133
nta::ECS::add_component
utils::Option< ComponentID > add_component(Entity entity, Args &&... args)
Definition: ECS.h:250
nta::ECS::num_components
std::size_t num_components() const
Returns the number of components of the given type.
Definition: ECS.h:179
nta::utils::SlotMap::deactivate
void deactivate(Key k)
Same thing as remove, but odes not bump the generation.
Definition: SlotMap.h:336
nta::ECS::get_component_list
ComponentList< T > & get_component_list() const
Returns a list of all components of the given type.
Definition: ECS.h:291
utils::TypeMap
Definition: TypeMap.h:74
nta::Component
Base class for components.
Definition: ECS.h:23
nta::ECS::for_each
void for_each(std::function< void(T &)> func) const
Runs a function of each Component of a given type.
Definition: ECS.h:307
nta::ECS::m_component_info
utils::SlotMap< ComponentInfo > m_component_info
Definition: ECS.h:153
nta::ECS
Definition: ECS.h:135
nta::utils::SlotMap
Definition: SlotMap.h:105
nta::ECS::delete_entity
bool delete_entity(Entity id)
Definition: ECS.cpp:16
nta::ComponentRegistry
A store of every Component type in use by an ECS.
Definition: ECS.h:37
nta::ECS::ComponentInfo
Info directly attached to a ComponentID.
Definition: ECS.h:138
nta::utils::IDFactory
Class for generating unique (integral) IDs.
Definition: IDFactory.h:14
nta::utils::Option
Definition: Option.h:17
nta::Component::Component
Component()
Create using ECS::add_component.
Definition: ECS.h:26
nta::utils::Option::none
static Option none()
Creates a None variant Option.
Definition: Option.h:50
nta::ECS::enact_on
void enact_on(const Event &event, Entity entity) const
Enacts the event on the Component of the given type owned by the given Entity.
Definition: ECS.h:314
nta::Component::get_id
const ComponentID get_id() const
Returns this Component's id.
Definition: ECS.h:32
nta::ECS::enact_on_sibling
void enact_on_sibling(const Event &event, ComponentID cmpn) const
Enacts the event on the Component of the given type owned by the same entity.
Definition: ECS.h:326
nta::ECS::get_sibling
utils::Option< T & > get_sibling(ComponentID cmpn) const
Returns the Component of the given type with the same owner as cmpn.
Definition: ECS.h:303
nta::ComponentRegistry::Record
Definition: ECS.h:39
nta::ECS::enact_on_all
void enact_on_all(const Event &event, typename Event::enum_type e) const
Enacts the event on all the Components of the given type.
Definition: ECS.h:338
nta
Definition: Animation2D.h:6
nta::ECS::m_cmpn_gen
utils::IDFactory< ComponentID > m_cmpn_gen
Responsible for creating unique IDs for the Components.
Definition: ECS.h:157
nta::ECS::has_component
bool has_component(Entity entity) const
Returns true if the given Entity has a Component of type T.
Definition: ECS.h:280
nta::ECS::m_registry
const ComponentRegistry m_registry
Definition: ECS.h:161
nta::ECS::does_entity_exist
bool does_entity_exist(Entity entity) const
Returns true if the given Entity exists.
Definition: ECS.cpp:55
nta::ECS::get_component
utils::Option< T & > get_component(Entity entity) const
Returns the Component of the given type associated to the given Entity.
Definition: ECS.h:298
nta::Component::m_id
ComponentID m_id
Unique identifier.
Definition: ECS.h:28
nta::ECS::gen_entity
Entity gen_entity()
Generates a new Entity, returning its ID.
Definition: ECS.cpp:9
nta::ECS::m_components
utils::TypeMap m_components
Definition: ECS.h:148
nta::ECS::delete_component
bool delete_component(ComponentID cmpn)
Definition: ECS.cpp:39
nta::ECS::clear
void clear()
Removes all entities and components from this system.
Definition: ECS.cpp:69
nta::ECS::num_entities
std::size_t num_entities() const
Returns the number of entities in the system.
Definition: ECS.h:176
nta::utils::SlotMapKey<>
nta::ECS::gen_entities
void gen_entities(std::size_t num, Entity *ids)
Generates several entities, storing their IDs in ids.
Definition: ECS.cpp:13
nta::ECS::get_owner
utils::Option< Entity > get_owner(ComponentID cmpn) const
Returns the Entity associated to this Component.
Definition: ECS.cpp:58
nta::ECS::m_entity_gen
utils::IDFactory< Entity > m_entity_gen
Responsible for creating unique IDs for the Entities.
Definition: ECS.h:155