From cb05c2cf1ddce52b930ee1a7adb2d611d0a32a77 Mon Sep 17 00:00:00 2001 From: NukeBird Date: Thu, 20 Feb 2025 02:39:24 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=A6Archetypes,=20entities,=20component?= =?UTF-8?q?s=20introspection=F0=9F=93=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/zecsy.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++++++++ zecsy.hpp | 65 +++++++++++++++++++++++ 2 files changed, 198 insertions(+) diff --git a/tests/zecsy.cpp b/tests/zecsy.cpp index 4bbe582..65a4d71 100644 --- a/tests/zecsy.cpp +++ b/tests/zecsy.cpp @@ -409,3 +409,136 @@ TEST_CASE("Systems handle negative delta time") REQUIRE(count == 2); } + +TEST_CASE("Entity count tracking") +{ + world w; + REQUIRE(w.entity_count() == 0); + + const auto e1 = w.make_entity(); + const auto e2 = w.make_entity(); + REQUIRE(w.entity_count() == 2); + + w.destroy_entity(e1); + REQUIRE(w.entity_count() == 1); +} + +TEST_CASE("Component counting mechanisms") +{ + struct Health + { + int value; + }; + + struct Position + { + float x, y; + }; + + world w; + auto e = w.make_entity(); + + REQUIRE(w.component_count() == 0); + REQUIRE(w.total_component_count() == 0); + + w.set(e); + REQUIRE(w.component_count() == 1); + REQUIRE(w.total_component_count() == 1); + + w.set(e); + REQUIRE(w.component_count() == 1); + REQUIRE(w.total_component_count() == 2); + + w.remove(e); + REQUIRE(w.component_count() == 0); + REQUIRE(w.total_component_count() == 1); +} + +TEST_CASE("Archetype signature management") +{ + struct A + { + }; + + struct B + { + }; + + struct C + { + }; + + world w; + + // Initial state: empty archetype + REQUIRE(w.archetype_count() == 0); + + auto e = w.make_entity(); + REQUIRE(w.archetype_count() == 1); + + // Add first component + w.set(e); + REQUIRE(w.archetype_count() == 1); +} + +TEST_CASE("Component distribution across archetypes") +{ + struct A + { + }; + + struct B + { + }; + + world w; + + // Create 10 entities in different configurations + for(int i = 0; i < 5; ++i) + { + auto e = w.make_entity(); + w.set(e); + } + for(int i = 0; i < 3; ++i) + { + auto e = w.make_entity(); + w.set(e); + } + for(int i = 0; i < 2; ++i) + { + auto e = w.make_entity(); + w.set(e); + } + + // Verify distribution + REQUIRE(w.entity_count() == 10); + REQUIRE(w.component_count() == 8); + REQUIRE(w.component_count() == 5); + REQUIRE(w.archetype_count() == 3); //, , +} + +TEST_CASE("Entity inspection") +{ + struct Transform + { + float x, y; + }; + + struct Renderable + { + }; + + world w; + auto e = w.make_entity(); + + REQUIRE(w.components_in_entity(e) == 0); + + w.set(e); + REQUIRE(w.components_in_entity(e) == 1); + + w.set(e); + REQUIRE(w.components_in_entity(e) == 2); + + w.remove(e); + REQUIRE(w.components_in_entity(e) == 1); +} diff --git a/zecsy.hpp b/zecsy.hpp index d9663b2..e879439 100644 --- a/zecsy.hpp +++ b/zecsy.hpp @@ -34,6 +34,14 @@ namespace zecsy entity_id make_entity(); void destroy_entity(entity_id e); bool is_alive(entity_id e) const; + size_t components_in_entity(entity_id e) const; + + size_t entity_count() const; + size_t total_component_count() const; + size_t archetype_count() const; + + template + size_t component_count(); template bool has(entity_id e) const; @@ -66,12 +74,18 @@ namespace zecsy template void query(std::invocable auto&& system); + size_t get_archetypes_checked() const; + size_t get_entities_processed() const; + private: using comp_id = size_t; std::set alive_entities; std::unordered_map> entity_to_comps; entity_id entity_counter = 0; + size_t query_archetypes_checked = 0; + size_t query_entities_processed = 0; + struct component_pool { std::vector data; @@ -119,6 +133,51 @@ namespace zecsy inline world::comp_id world::next_component_id = 0; + inline size_t world::components_in_entity(entity_id e) const + { + return entity_to_comps.contains(e) ? entity_to_comps.at(e).size() : 0; + } + + inline size_t world::entity_count() const + { + return alive_entities.size(); + } + + inline size_t world::total_component_count() const + { + size_t count = 0; + + for(const auto& [id, pool]: pools) + { + count += pool.entity_to_index.size(); + } + + return count; + } + + inline size_t world::archetype_count() const + { + return archetypes.size(); + } + + template + inline size_t world::component_count() + { + const comp_id id = get_component_id(); + const auto it = pools.find(id); + return it != pools.end() ? it->second.entity_to_index.size() : 0; + } + + inline size_t world::get_archetypes_checked() const + { + return query_archetypes_checked; + } + + inline size_t world::get_entities_processed() const + { + return query_entities_processed; + } + inline entity_id world::make_entity() { auto id = ++entity_counter; @@ -305,8 +364,13 @@ namespace zecsy std::vector required = {get_component_id()...}; std::sort(required.begin(), required.end()); + query_archetypes_checked = 0; + query_entities_processed = 0; + for(const auto& [archetype_key, entities]: archetypes) { + query_archetypes_checked++; + bool match = true; for(comp_id req_id: required) { @@ -320,6 +384,7 @@ namespace zecsy if(match) { + query_entities_processed += entities.size(); for(entity_id e: entities) { system(e, get(e)...);