Bye-bye entity wrapper

This commit is contained in:
NukeBird 2025-02-15 22:33:02 +03:00
parent 12f2bf3474
commit 3b8234655d
2 changed files with 46 additions and 125 deletions

View file

@ -15,7 +15,6 @@ TEST_CASE("Create a single entity and verify its existence")
auto e = w.make_entity(); auto e = w.make_entity();
REQUIRE(w.is_alive(e)); REQUIRE(w.is_alive(e));
REQUIRE(e.is_alive());
} }
TEST_CASE("Destroy an entity and ensure it no longer exists in the world") TEST_CASE("Destroy an entity and ensure it no longer exists in the world")
@ -26,7 +25,6 @@ TEST_CASE("Destroy an entity and ensure it no longer exists in the world")
w.destroy_entity(e); w.destroy_entity(e);
REQUIRE_FALSE(w.is_alive(e)); REQUIRE_FALSE(w.is_alive(e));
REQUIRE_FALSE(e.is_alive());
} }
TEST_CASE("Entity #0 should be reserved and never used") TEST_CASE("Entity #0 should be reserved and never used")
@ -36,8 +34,7 @@ TEST_CASE("Entity #0 should be reserved and never used")
auto e = w.make_entity(); auto e = w.make_entity();
REQUIRE(e != 0); REQUIRE(e != 0);
REQUIRE(entity() == 0); REQUIRE_FALSE(w.is_alive(0));
REQUIRE_FALSE(entity().is_alive());
} }
struct ChoosenOne struct ChoosenOne
@ -52,7 +49,6 @@ TEST_CASE("Entity shouldn't have a component that wasn't attached to it")
auto e = w.make_entity(); auto e = w.make_entity();
REQUIRE_FALSE(w.has<ChoosenOne>(e)); REQUIRE_FALSE(w.has<ChoosenOne>(e));
REQUIRE_FALSE(e.has<ChoosenOne>());
} }
TEST_CASE("Attempt of getting non-owned component should throw") TEST_CASE("Attempt of getting non-owned component should throw")
@ -62,7 +58,6 @@ TEST_CASE("Attempt of getting non-owned component should throw")
auto e = w.make_entity(); auto e = w.make_entity();
REQUIRE_THROWS(w.get<ChoosenOne>(e)); REQUIRE_THROWS(w.get<ChoosenOne>(e));
REQUIRE_THROWS(e.get<ChoosenOne>());
} }
TEST_CASE("Attach a simple component to an entity and verify it is correctly associated") TEST_CASE("Attach a simple component to an entity and verify it is correctly associated")
@ -70,15 +65,13 @@ TEST_CASE("Attach a simple component to an entity and verify it is correctly ass
world w; world w;
auto e1 = w.make_entity(); auto e1 = w.make_entity();
e1.set(ChoosenOne{}); w.set(e1, ChoosenOne{});
REQUIRE(e1.has<ChoosenOne>());
REQUIRE(w.has<ChoosenOne>(e1)); REQUIRE(w.has<ChoosenOne>(e1));
auto e2 = w.make_entity(); auto e2 = w.make_entity();
w.set(e2, ChoosenOne{}); w.set(e2, ChoosenOne{});
REQUIRE(e2.has<ChoosenOne>());
REQUIRE(w.has<ChoosenOne>(e2)); REQUIRE(w.has<ChoosenOne>(e2));
} }
@ -92,13 +85,13 @@ TEST_CASE("Retrieve a component from an entity and verify its data matches what
world w; world w;
auto e = w.make_entity(); auto e = w.make_entity();
e.set(Name{"zecsy!"}); w.set(e, Name{"zecsy!"});
REQUIRE(e.get<Name>().value == "zecsy!"); REQUIRE(w.get<Name>(e).value == "zecsy!");
e.get<Name>().value = "super-zecsy!"; w.get<Name>(e).value = "super-zecsy!";
REQUIRE(e.get<Name>().value == "super-zecsy!"); REQUIRE(w.get<Name>(e).value == "super-zecsy!");
} }
TEST_CASE("Remove a component from an entity and verify it is no longer attached") TEST_CASE("Remove a component from an entity and verify it is no longer attached")
@ -106,12 +99,12 @@ TEST_CASE("Remove a component from an entity and verify it is no longer attached
world w; world w;
auto e = w.make_entity(); auto e = w.make_entity();
e.set(ChoosenOne{}); w.set(e, ChoosenOne{});
REQUIRE_NOTHROW(e.remove<ChoosenOne>()); REQUIRE_NOTHROW(w.remove<ChoosenOne>(e));
REQUIRE_FALSE(e.has<ChoosenOne>()); REQUIRE_FALSE(w.has<ChoosenOne>(e));
e.set(ChoosenOne{}); w.set(e, ChoosenOne{});
REQUIRE_NOTHROW(w.remove<ChoosenOne>(e)); REQUIRE_NOTHROW(w.remove<ChoosenOne>(e));
REQUIRE_FALSE(w.has<ChoosenOne>(e)); REQUIRE_FALSE(w.has<ChoosenOne>(e));
@ -120,7 +113,7 @@ TEST_CASE("Remove a component from an entity and verify it is no longer attached
TEST_CASE("Addresses of removed components should be reused") TEST_CASE("Addresses of removed components should be reused")
{ {
world w; world w;
std::vector<entity> entities; std::vector<entity_id> entities;
std::vector<ChoosenOne*> addr; std::vector<ChoosenOne*> addr;
const int N = 4; const int N = 4;
@ -130,27 +123,27 @@ TEST_CASE("Addresses of removed components should be reused")
for(int j = 0; j < N; ++j) for(int j = 0; j < N; ++j)
{ {
entities.emplace_back(w.make_entity()); entities.emplace_back(w.make_entity());
entities.back().set<ChoosenOne>(); w.set<ChoosenOne>(entities.back());
} }
if(addr.empty()) if(addr.empty())
{ {
for(int j = 0; j < N; ++j) for(int j = 0; j < N; ++j)
{ {
addr.emplace_back(&entities[j].get<ChoosenOne>()); addr.emplace_back(&w.get<ChoosenOne>(entities[j]));
} }
} }
else else
{ {
for(int j = 0; j < N; ++j) for(int j = 0; j < N; ++j)
{ {
REQUIRE(&entities[j].get<ChoosenOne>() == addr[j]); REQUIRE(&w.get<ChoosenOne>(entities[j]) == addr[j]);
} }
} }
for(auto e: entities) for(auto e: entities)
{ {
e.remove<ChoosenOne>(); w.remove<ChoosenOne>(e);
} }
entities.clear(); entities.clear();
} }
@ -161,17 +154,15 @@ TEST_CASE("Attach multiple components to an entity and verify all are correctly
world w; world w;
auto e = w.make_entity(); auto e = w.make_entity();
e.set(ChoosenOne{}, Name{"zecsy"}); w.set(e, ChoosenOne{}, Name{"zecsy"});
REQUIRE(e.has<ChoosenOne, Name>());
REQUIRE(w.has<ChoosenOne, Name>(e)); REQUIRE(w.has<ChoosenOne, Name>(e));
e.remove<ChoosenOne, Name>(); w.remove<ChoosenOne, Name>(e);
REQUIRE_FALSE(e.has<ChoosenOne, Name>());
REQUIRE_FALSE(w.has<ChoosenOne, Name>(e)); REQUIRE_FALSE(w.has<ChoosenOne, Name>(e));
REQUIRE_FALSE(e.has<ChoosenOne>()); REQUIRE_FALSE(w.has<ChoosenOne>(e));
REQUIRE_FALSE(e.has<Name>()); REQUIRE_FALSE(w.has<Name>(e));
} }
TEST_CASE("Create a simple system that processes entities with a specific component and verify it executes correctly") TEST_CASE("Create a simple system that processes entities with a specific component and verify it executes correctly")
@ -184,11 +175,11 @@ TEST_CASE("Create a simple system that processes entities with a specific compon
world w; world w;
auto e0 = w.make_entity(), e1 = w.make_entity(); auto e0 = w.make_entity(), e1 = w.make_entity();
e0.set<Component>(); //or e0.set(Component{}) w.set<Component>(e0); //or e0.set(Component{})
e1.set(Component{20}); w.set(e1, Component{20});
REQUIRE(e0.get<Component>().value == 0); REQUIRE(w.get<Component>(e0).value == 0);
REQUIRE(e1.get<Component>().value == 20); REQUIRE(w.get<Component>(e1).value == 20);
/* /*
* Really wanna deduce it to w.query([](Component&){}), * Really wanna deduce it to w.query([](Component&){}),
@ -199,8 +190,8 @@ TEST_CASE("Create a simple system that processes entities with a specific compon
c.value++; c.value++;
}); });
REQUIRE(e0.get<Component>().value == 1); REQUIRE(w.get<Component>(e0).value == 1);
REQUIRE(e1.get<Component>().value == 21); REQUIRE(w.get<Component>(e1).value == 21);
} }
TEST_CASE("Test a systems ability to query and process only entities with a specific combination of components") TEST_CASE("Test a systems ability to query and process only entities with a specific combination of components")
@ -218,18 +209,18 @@ TEST_CASE("Test a systems ability to query and process only entities with a s
world w; world w;
auto e0 = w.make_entity(); auto e0 = w.make_entity();
e0.set(C0{}, C1{}); w.set(e0, C0{}, C1{});
auto e1 = w.make_entity(); auto e1 = w.make_entity();
e1.set(C0{}); w.set(e1, C0{});
auto e2 = w.make_entity(); auto e2 = w.make_entity();
e2.set(C1{}); w.set(e2, C1{});
int count = 0; int count = 0;
REQUIRE(e0.get<C0>().value == 0); REQUIRE(w.get<C0>(e0).value == 0);
REQUIRE(e0.get<C1>().value == 10); REQUIRE(w.get<C1>(e0).value == 10);
w.query<C0, C1>([&count](C0& c0, C1& c1) w.query<C0, C1>([&count](C0& c0, C1& c1)
{ {
@ -237,12 +228,12 @@ TEST_CASE("Test a systems ability to query and process only entities with a s
c1.value++; c1.value++;
}); });
REQUIRE(e0.get<C0>().value == 1); REQUIRE(w.get<C0>(e0).value == 1);
REQUIRE(e0.get<C1>().value == 11); REQUIRE(w.get<C1>(e0).value == 11);
REQUIRE(e1.get<C0>().value == 0); REQUIRE(w.get<C0>(e1).value == 0);
REQUIRE(e2.get<C1>().value == 10); REQUIRE(w.get<C1>(e2).value == 10);
REQUIRE_FALSE(e1.has<C1>()); REQUIRE_FALSE(w.has<C1>(e1));
REQUIRE_FALSE(e2.has<C0>()); REQUIRE_FALSE(w.has<C0>(e2));
} }

View file

@ -1,8 +1,6 @@
#pragma once #pragma once
#include <catch2/internal/catch_console_colour.hpp>
#include <cstdint> #include <cstdint>
#include <format> #include <format>
#include <functional>
#include <queue> #include <queue>
#include <stdexcept> #include <stdexcept>
#include <typeindex> #include <typeindex>
@ -22,36 +20,7 @@ namespace zecsy
*/ */
using entities_set = std::set<entity_id>; using entities_set = std::set<entity_id>;
class entity final class component_storage final
{
public:
entity(class world* w, entity_id id);
entity() = default;
operator entity_id() const;
bool is_alive() const;
template<typename... T>
bool has() const;
template<typename T>
T& get();
template<typename... T>
void set();
template<typename... T>
void set(const T&... comps);
template<typename... T>
void remove();
private:
entity_id id = 0;
class world* w = nullptr;
};
class component_storage
{ {
public: public:
template<typename T> template<typename T>
@ -104,7 +73,7 @@ namespace zecsy
class world final class world final
{ {
public: public:
entity 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;
@ -131,27 +100,12 @@ namespace zecsy
component_storage storage; component_storage storage;
}; };
inline entity::entity(class world* w, entity_id id): w(w), id(id) inline entity_id world::make_entity()
{
}
inline entity::operator entity_id() const
{
return id;
}
inline bool entity::is_alive() const
{
return w && w->is_alive(id);
}
inline entity world::make_entity()
{ {
auto id = ++entity_counter; auto id = ++entity_counter;
alive_entities.emplace(id); alive_entities.emplace(id);
return entity(this, id); return id;
} }
inline void world::destroy_entity(entity_id e) inline void world::destroy_entity(entity_id e)
@ -191,30 +145,12 @@ namespace zecsy
return *ptr; return *ptr;
} }
template<typename... T>
inline bool entity::has() const
{
return w->has<T...>(id);
}
template<typename T> template<typename T>
inline T& world::get(entity_id e) inline T& world::get(entity_id e)
{ {
return storage.get<T>(e); return storage.get<T>(e);
} }
template<typename T>
inline T& entity::get()
{
return w->get<T>(id);
}
template<typename... T>
inline void entity::set()
{
(set(T{}), ...);
}
template<typename T> template<typename T>
inline void component_storage::set(entity_id e, const T& comp) inline void component_storage::set(entity_id e, const T& comp)
{ {
@ -246,6 +182,12 @@ namespace zecsy
indices_dict[typeid(T)][e] = index; indices_dict[typeid(T)][e] = index;
} }
template<typename T>
inline void component_storage::set(entity_id e)
{
set(e, T{});
}
template<typename... T> template<typename... T>
inline void world::set(entity_id e) inline void world::set(entity_id e)
{ {
@ -258,12 +200,6 @@ namespace zecsy
storage.set(e, comps...); storage.set(e, comps...);
} }
template<typename... T>
inline void entity::set(const T&... comps)
{
w->set(id, comps...);
}
template<typename T> template<typename T>
inline void component_storage::remove(entity_id e) inline void component_storage::remove(entity_id e)
{ {
@ -283,12 +219,6 @@ namespace zecsy
storage.remove<T...>(e); storage.remove<T...>(e);
} }
template<typename... T>
inline void entity::remove()
{
w->remove<T...>(id);
}
template<typename... T> template<typename... T>
inline bool world::has(entity_id e) const inline bool world::has(entity_id e) const
{ {