From 21c7e416ecaceb511d4fc275762219b1a0ec7262 Mon Sep 17 00:00:00 2001 From: NukeBird Date: Fri, 14 Feb 2025 23:42:01 +0300 Subject: [PATCH] Attach simple components to an entity --- tests/zecsy.cpp | 46 ++++++++++++++++- zecsy.hpp | 131 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 174 insertions(+), 3 deletions(-) diff --git a/tests/zecsy.cpp b/tests/zecsy.cpp index 0174c37..2659208 100644 --- a/tests/zecsy.cpp +++ b/tests/zecsy.cpp @@ -2,7 +2,7 @@ #define CATCH_CONFIG_MAIN #include -#define MAX_ZECSY_ENTITIES 4 +//#define MAX_ZECSY_ENTITIES 4 #include "../zecsy.hpp" using namespace zecsy; @@ -45,8 +45,50 @@ TEST_CASE("World should throw on id overflow") for(int i = 0; i < MAX_ZECSY_ENTITIES; ++i) { - REQUIRE_NOTHROW(w.make_entity()); + w.make_entity(); } REQUIRE_THROWS(w.make_entity()); } + +struct ChoosenOne +{ + +}; + +TEST_CASE("Entity shouldn't have a component that wasn't attached to it") +{ + world w; + + auto e = w.make_entity(); + + REQUIRE_FALSE(w.has(e)); + REQUIRE_FALSE(e.has()); +} + +TEST_CASE("Attempt of getting non-owned component should throw") +{ + world w; + + auto e = w.make_entity(); + + REQUIRE_THROWS(w.get(e)); + REQUIRE_THROWS(e.get()); +} + +TEST_CASE("Attach a simple component to an entity and verify it is correctly associated") +{ + world w; + + auto e1 = w.make_entity(); + e1.set(ChoosenOne{}); + + REQUIRE(e1.has()); + REQUIRE(w.has(e1)); + + auto e2 = w.make_entity(); + w.set(e2, ChoosenOne{}); + + REQUIRE(e2.has()); + REQUIRE(w.has(e2)); +} diff --git a/zecsy.hpp b/zecsy.hpp index 3ff0be3..7b5fe27 100644 --- a/zecsy.hpp +++ b/zecsy.hpp @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #ifndef MAX_ZECSY_ENTITIES #define MAX_ZECSY_ENTITIES 65536 @@ -26,11 +29,49 @@ namespace zecsy operator entity_id() const; bool is_alive() const; + + template + bool has() const; + + template + T& get(); + + template + void set(const T& comp = T{}); private: entity_id id = 0; class world* w = nullptr; }; + using zecsy_bits = std::bitset; + + class component_storage + { + public: + template + bool has(entity_id e) const; + + template + T& get(entity_id e); + + template + void set(entity_id e, const T& comp = T{}); + private: + using comp_index = std::type_index; + using comp_to_bitset = std::unordered_map; + using entity_to_index = std::unordered_map; + + using comp_to_entity_dict = std::unordered_map; + + using comp_to_storage = std::unordered_map>; + + comp_to_bitset bitset_dict; + comp_to_entity_dict entity_dict; + comp_to_storage storage_dict; + }; + class world final { public: @@ -43,9 +84,19 @@ namespace zecsy entity make_entity(); void destroy_entity(entity_id e); bool is_alive(entity_id e) const; + + template + bool has(entity_id e) const; + + template + T& get(entity_id e); + + template + void set(entity_id e, const T& comp = T{}); private: - std::bitset entities_bitset; + zecsy_bits entities_bitset; entity_id entity_counter = 0; + component_storage storage; }; inline entity::entity(class world* w, entity_id id): w(w), id(id) @@ -86,4 +137,82 @@ namespace zecsy { return entities_bitset.test(e); } + + template + inline bool world::has(entity_id e) const + { + return storage.has(e); + } + + template + inline bool component_storage::has(entity_id e) const + { + if(bitset_dict.contains(typeid(T))) + { + return bitset_dict.at(typeid(T)).test(e); + } + return false; + } + + template + inline T& component_storage::get(entity_id e) + { + if(!has(e)) + { + throw std::runtime_error(std::format("Entity #{} doesn't have {}", + e, typeid(T).name())); + } + + auto index = entity_dict[typeid(T)].at(e); + + auto* ptr = reinterpret_cast(&storage_dict[typeid(T)][0]); + ptr += index; + + return *ptr; + } + + template + inline bool entity::has() const + { + return w->has(id); + } + + template + inline T& world::get(entity_id e) + { + return storage.get(e); + } + + template + inline T& entity::get() + { + return w->get(id); + } + + template + inline void component_storage::set(entity_id id, const T& comp) + { + bitset_dict[typeid(T)].set(id); + + auto& storage = storage_dict[typeid(T)]; + + size_t T_size = sizeof(T); + size_t old_size = storage.size(); + + storage.resize(old_size + T_size); + void* ptr = &storage[0] + old_size; + new(ptr) T(comp); + } + + template + inline void world::set(entity_id e, const T& comp) + { + storage.set(e, comp); + } + + template + inline void entity::set(const T& comp) + { + w->set(id, comp); + } };