📦Archetypes, entities, components introspection📦

This commit is contained in:
NukeBird 2025-02-20 02:39:24 +03:00
parent 8055ac0cac
commit cb05c2cf1d
2 changed files with 198 additions and 0 deletions

View file

@ -409,3 +409,136 @@ TEST_CASE("Systems handle negative delta time")
REQUIRE(count == 2); 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<Health>() == 0);
REQUIRE(w.total_component_count() == 0);
w.set<Health>(e);
REQUIRE(w.component_count<Health>() == 1);
REQUIRE(w.total_component_count() == 1);
w.set<Position>(e);
REQUIRE(w.component_count<Position>() == 1);
REQUIRE(w.total_component_count() == 2);
w.remove<Health>(e);
REQUIRE(w.component_count<Health>() == 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<A>(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<A>(e);
}
for(int i = 0; i < 3; ++i)
{
auto e = w.make_entity();
w.set<A, B>(e);
}
for(int i = 0; i < 2; ++i)
{
auto e = w.make_entity();
w.set<B>(e);
}
// Verify distribution
REQUIRE(w.entity_count() == 10);
REQUIRE(w.component_count<A>() == 8);
REQUIRE(w.component_count<B>() == 5);
REQUIRE(w.archetype_count() == 3); //<A>, <A, B>, <B>
}
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<Transform>(e);
REQUIRE(w.components_in_entity(e) == 1);
w.set<Renderable>(e);
REQUIRE(w.components_in_entity(e) == 2);
w.remove<Transform>(e);
REQUIRE(w.components_in_entity(e) == 1);
}

View file

@ -34,6 +34,14 @@ namespace zecsy
entity_id make_entity(); entity_id make_entity();
void destroy_entity(entity_id e); void destroy_entity(entity_id e);
bool is_alive(entity_id e) const; 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<Component T>
size_t component_count();
template<Component T> template<Component T>
bool has(entity_id e) const; bool has(entity_id e) const;
@ -66,12 +74,18 @@ namespace zecsy
template<Component... T> template<Component... T>
void query(std::invocable<entity_id, T&...> auto&& system); void query(std::invocable<entity_id, T&...> auto&& system);
size_t get_archetypes_checked() const;
size_t get_entities_processed() const;
private: private:
using comp_id = size_t; using comp_id = size_t;
std::set<entity_id> alive_entities; std::set<entity_id> alive_entities;
std::unordered_map<entity_id, std::set<comp_id>> entity_to_comps; std::unordered_map<entity_id, std::set<comp_id>> entity_to_comps;
entity_id entity_counter = 0; entity_id entity_counter = 0;
size_t query_archetypes_checked = 0;
size_t query_entities_processed = 0;
struct component_pool struct component_pool
{ {
std::vector<uint8_t> data; std::vector<uint8_t> data;
@ -119,6 +133,51 @@ namespace zecsy
inline world::comp_id world::next_component_id = 0; 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<Component T>
inline size_t world::component_count()
{
const comp_id id = get_component_id<T>();
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() inline entity_id world::make_entity()
{ {
auto id = ++entity_counter; auto id = ++entity_counter;
@ -305,8 +364,13 @@ namespace zecsy
std::vector<comp_id> required = {get_component_id<T>()...}; std::vector<comp_id> required = {get_component_id<T>()...};
std::sort(required.begin(), required.end()); std::sort(required.begin(), required.end());
query_archetypes_checked = 0;
query_entities_processed = 0;
for(const auto& [archetype_key, entities]: archetypes) for(const auto& [archetype_key, entities]: archetypes)
{ {
query_archetypes_checked++;
bool match = true; bool match = true;
for(comp_id req_id: required) for(comp_id req_id: required)
{ {
@ -320,6 +384,7 @@ namespace zecsy
if(match) if(match)
{ {
query_entities_processed += entities.size();
for(entity_id e: entities) for(entity_id e: entities)
{ {
system(e, get<T>(e)...); system(e, get<T>(e)...);