Attach simple components to an entity
This commit is contained in:
parent
f1f965a284
commit
21c7e416ec
2 changed files with 174 additions and 3 deletions
|
@ -2,7 +2,7 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#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<ChoosenOne>(e));
|
||||
REQUIRE_FALSE(e.has<ChoosenOne>());
|
||||
}
|
||||
|
||||
TEST_CASE("Attempt of getting non-owned component should throw")
|
||||
{
|
||||
world w;
|
||||
|
||||
auto e = w.make_entity();
|
||||
|
||||
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")
|
||||
{
|
||||
world w;
|
||||
|
||||
auto e1 = w.make_entity();
|
||||
e1.set(ChoosenOne{});
|
||||
|
||||
REQUIRE(e1.has<ChoosenOne>());
|
||||
REQUIRE(w.has<ChoosenOne>(e1));
|
||||
|
||||
auto e2 = w.make_entity();
|
||||
w.set(e2, ChoosenOne{});
|
||||
|
||||
REQUIRE(e2.has<ChoosenOne>());
|
||||
REQUIRE(w.has<ChoosenOne>(e2));
|
||||
}
|
||||
|
|
131
zecsy.hpp
131
zecsy.hpp
|
@ -3,6 +3,9 @@
|
|||
#include <bitset>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <new>
|
||||
|
||||
#ifndef MAX_ZECSY_ENTITIES
|
||||
#define MAX_ZECSY_ENTITIES 65536
|
||||
|
@ -26,11 +29,49 @@ namespace zecsy
|
|||
operator entity_id() const;
|
||||
|
||||
bool is_alive() const;
|
||||
|
||||
template<typename T>
|
||||
bool has() const;
|
||||
|
||||
template<typename T>
|
||||
T& get();
|
||||
|
||||
template<typename T>
|
||||
void set(const T& comp = T{});
|
||||
private:
|
||||
entity_id id = 0;
|
||||
class world* w = nullptr;
|
||||
};
|
||||
|
||||
using zecsy_bits = std::bitset<MAX_ZECSY_ENTITIES + 1>;
|
||||
|
||||
class component_storage
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
bool has(entity_id e) const;
|
||||
|
||||
template<typename T>
|
||||
T& get(entity_id e);
|
||||
|
||||
template<typename T>
|
||||
void set(entity_id e, const T& comp = T{});
|
||||
private:
|
||||
using comp_index = std::type_index;
|
||||
using comp_to_bitset = std::unordered_map<comp_index, zecsy_bits>;
|
||||
using entity_to_index = std::unordered_map<entity_id, size_t>;
|
||||
|
||||
using comp_to_entity_dict = std::unordered_map<comp_index,
|
||||
entity_to_index>;
|
||||
|
||||
using comp_to_storage = std::unordered_map<comp_index,
|
||||
std::vector<uint8_t>>;
|
||||
|
||||
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<typename T>
|
||||
bool has(entity_id e) const;
|
||||
|
||||
template<typename T>
|
||||
T& get(entity_id e);
|
||||
|
||||
template<typename T>
|
||||
void set(entity_id e, const T& comp = T{});
|
||||
private:
|
||||
std::bitset<MAX_ZECSY_ENTITIES + 1> 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<typename T>
|
||||
inline bool world::has(entity_id e) const
|
||||
{
|
||||
return storage.has<T>(e);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
inline T& component_storage::get(entity_id e)
|
||||
{
|
||||
if(!has<T>(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<T*>(&storage_dict[typeid(T)][0]);
|
||||
ptr += index;
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool entity::has() const
|
||||
{
|
||||
return w->has<T>(id);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T& world::get(entity_id e)
|
||||
{
|
||||
return storage.get<T>(e);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T& entity::get()
|
||||
{
|
||||
return w->get<T>(id);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
inline void world::set(entity_id e, const T& comp)
|
||||
{
|
||||
storage.set(e, comp);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void entity::set(const T& comp)
|
||||
{
|
||||
w->set(id, comp);
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue