A 2D tile-based sandbox game.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Collection.h 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #pragma once
  2. #include <vector>
  3. #include <string>
  4. #include <typeindex>
  5. #include <type_traits>
  6. #include "common.h"
  7. #include "log.h"
  8. #include "Entity.h"
  9. #include "SlotVector.h"
  10. #include "SmallOptional.h"
  11. namespace Swan {
  12. class EntityCollection;
  13. class EntityRef {
  14. public:
  15. EntityRef() = default;
  16. EntityRef(EntityCollection *coll, size_t index, size_t generation):
  17. coll_(coll), index_(index), generation_(generation) {}
  18. template<typename Func>
  19. EntityRef &then(Func func);
  20. bool hasValue();
  21. Entity *get();
  22. private:
  23. EntityCollection *coll_;
  24. size_t index_;
  25. size_t generation_;
  26. };
  27. class EntityCollection {
  28. public:
  29. struct Factory {
  30. const std::string name;
  31. std::unique_ptr<EntityCollection> (*const create)(std::string name);
  32. };
  33. template<typename Ent>
  34. using OptEnt = SmallOptional<
  35. Ent, SmallOptionalInitialBytesPolicy<Ent, sizeof(void *)>>;
  36. template<typename Ent>
  37. using SlotPolicy = SlotVectorDefaultSentinel<SmallNullOpt>;
  38. virtual ~EntityCollection() = default;
  39. template<typename Ent, typename... Args>
  40. EntityRef spawn(Args&&... args);
  41. virtual const std::string &name() = 0;
  42. virtual std::type_index type() = 0;
  43. virtual size_t size() = 0;
  44. virtual Entity *get(size_t idx) = 0;
  45. virtual Entity *get(size_t idx, size_t generation) = 0;
  46. virtual EntityRef spawn(const Context &ctx, const Entity::PackObject &obj) = 0;
  47. virtual void update(const Context &ctx, float dt) = 0;
  48. virtual void tick(const Context &ctx, float dt) = 0;
  49. virtual void draw(const Context &ctx, Win &win) = 0;
  50. virtual void erase(size_t idx, size_t generation) = 0;
  51. private:
  52. virtual void *getEntityVector() = 0;
  53. virtual size_t nextGeneration() = 0;
  54. };
  55. template<typename Ent>
  56. class EntityCollectionImpl: public EntityCollection {
  57. public:
  58. EntityCollectionImpl(std::string name): name_(std::move(name)) {}
  59. size_t size() override { return entities_.size(); }
  60. Entity *get(size_t idx) override { return entities_[idx].get(); }
  61. Entity *get(size_t idx, size_t generation) override;
  62. const std::string &name() override { return name_; }
  63. std::type_index type() override { return typeid(Ent); }
  64. EntityRef spawn(const Context &ctx, const Entity::PackObject &obj) override;
  65. void update(const Context &ctx, float dt) override;
  66. void tick(const Context &ctx, float dt) override;
  67. void draw(const Context &ctx, Win &win) override;
  68. void erase(size_t idx, size_t generation) override;
  69. private:
  70. void *getEntityVector() override { return (void *)&entities_; }
  71. size_t nextGeneration() override { return generation_++; }
  72. const std::string name_;
  73. SlotVector<OptEnt<Ent>, SlotPolicy<Ent>> entities_;
  74. size_t generation_ = 0;
  75. };
  76. /*
  77. * EntityRef
  78. */
  79. template<typename Func>
  80. inline EntityRef &EntityRef::then(Func func) {
  81. Entity *ent = coll_->get(index_, generation_);
  82. if (ent != nullptr)
  83. func(ent);
  84. return *this;
  85. }
  86. inline Entity *EntityRef::get() {
  87. return coll_->get(index_, generation_);
  88. }
  89. inline bool EntityRef::hasValue() {
  90. return coll_->get(index_, generation_) != nullptr;
  91. }
  92. /*
  93. * EntityCollection
  94. */
  95. template<typename Ent, typename... Args>
  96. inline EntityRef EntityCollection::spawn(Args&&... args) {
  97. auto entities = (SlotVector<OptEnt<Ent>, SlotPolicy<Ent>> *)getEntityVector();
  98. size_t generation = nextGeneration();
  99. size_t idx = entities->emplace();
  100. OptEnt<Ent> &ent = (*entities)[idx];
  101. ent.emplace(std::forward<Args>(args)...);
  102. ent->index_ = idx;
  103. ent->generation_ = generation;
  104. if constexpr (std::is_base_of_v<BodyTrait, Ent>) {
  105. BodyTrait::Body &body = ent->get(BodyTrait::Tag{});
  106. body.pos -= body.size / 2;
  107. }
  108. return { this, idx, generation };
  109. }
  110. /*
  111. * EntityCollectionImpl
  112. */
  113. template<typename Ent>
  114. inline Entity *EntityCollectionImpl<Ent>::get(size_t idx, size_t generation) {
  115. if (idx >= entities_.size())
  116. return nullptr;
  117. auto &e = entities_[idx];
  118. // We don't even need to check if e.hasValue(), because if it doesn't,
  119. // its generation will be 0xffff... and the check will fail
  120. if (e->generation_ != generation)
  121. return nullptr;
  122. return e.get();
  123. }
  124. template<typename Ent>
  125. inline EntityRef EntityCollectionImpl<Ent>::spawn(const Context &ctx, const Entity::PackObject &obj) {
  126. size_t generation = nextGeneration();
  127. size_t idx = entities_.emplace();
  128. OptEnt<Ent> &ent = entities_[idx];
  129. ent.emplace(ctx, obj);
  130. ent->index_ = idx;
  131. ent->generation_ = generation;
  132. return { this, idx, generation };
  133. }
  134. template<typename Ent>
  135. inline void EntityCollectionImpl<Ent>::update(const Context &ctx, float dt) {
  136. ZoneScopedN(typeid(Ent).name());
  137. for (auto &ent: entities_) {
  138. ZoneScopedN("update");
  139. ent->update(ctx, dt);
  140. }
  141. }
  142. template<typename Ent>
  143. inline void EntityCollectionImpl<Ent>::tick(const Context &ctx, float dt) {
  144. ZoneScopedN(typeid(Ent).name());
  145. for (auto &ent: entities_) {
  146. ZoneScopedN("tick");
  147. ent->tick(ctx, dt);
  148. }
  149. }
  150. template<typename Ent>
  151. inline void EntityCollectionImpl<Ent>::draw(const Context &ctx, Win &win) {
  152. ZoneScopedN(typeid(Ent).name());
  153. for (auto &ent: entities_) {
  154. ZoneScopedN("draw");
  155. ent->draw(ctx, win);
  156. }
  157. }
  158. template<typename Ent>
  159. inline void EntityCollectionImpl<Ent>::erase(size_t idx, size_t generation) {
  160. ZoneScopedN(typeid(Ent).name());
  161. OptEnt<Ent> &ent = entities_[idx];
  162. if (!ent.hasValue() || ent->generation_ != generation) {
  163. warn << "Erasing wrong entity " << typeid(Ent).name() << '[' << idx << "]!";
  164. return;
  165. }
  166. entities_.erase(idx);
  167. }
  168. }