diff --git a/ecs.hpp b/ecs.hpp index c2180a3..629fecd 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -31,6 +31,11 @@ namespace ecs_hpp class entity; class const_entity; + template < typename T > + class component; + template < typename T > + class const_component; + class system; class registry; @@ -662,6 +667,9 @@ namespace ecs_hpp { class entity final { public: + entity(const entity&) = default; + entity& operator=(const entity&) = default; + entity(registry& owner) noexcept; entity(registry& owner, entity_id id) noexcept; @@ -711,7 +719,10 @@ namespace ecs_hpp }; bool operator==(const entity& l, const entity& r) noexcept; + bool operator==(const entity& l, const const_entity& r) noexcept; + bool operator!=(const entity& l, const entity& r) noexcept; + bool operator!=(const entity& l, const const_entity& r) noexcept; } namespace std @@ -738,6 +749,9 @@ namespace ecs_hpp { class const_entity final { public: + const_entity(const const_entity&) = default; + const_entity& operator=(const const_entity&) = default; + const_entity(const entity& ent) noexcept; const_entity(const registry& owner) noexcept; @@ -767,7 +781,10 @@ namespace ecs_hpp entity_id id_{0u}; }; + bool operator==(const const_entity& l, const entity& r) noexcept; bool operator==(const const_entity& l, const const_entity& r) noexcept; + + bool operator!=(const const_entity& l, const entity& r) noexcept; bool operator!=(const const_entity& l, const const_entity& r) noexcept; } @@ -785,6 +802,113 @@ namespace std }; } +// ----------------------------------------------------------------------------- +// +// component +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + template < typename T > + class component final { + public: + component(const component&) = default; + component& operator=(const component&) = default; + + component(const entity& owner) noexcept; + + entity& owner() noexcept; + const entity& owner() const noexcept; + + bool remove(); + bool exists() const noexcept; + + template < typename... Args > + bool assign(Args&&... args); + + T& get(); + const T& get() const; + + T* find() noexcept; + const T* find() const noexcept; + private: + entity owner_; + }; + + template < typename T > + bool operator==(const component& l, const component& r) noexcept; + template < typename T > + bool operator==(const component& l, const const_component& r) noexcept; + + template < typename T > + bool operator!=(const component& l, const component& r) noexcept; + template < typename T > + bool operator!=(const component& l, const const_component& r) noexcept; +} + +namespace std +{ + template < typename T > + struct hash> + : std::unary_function&, std::size_t> + { + std::size_t operator()(const ecs_hpp::component& comp) const noexcept { + return std::hash()(comp.owner()); + } + }; +} + +// ----------------------------------------------------------------------------- +// +// const_component +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + template < typename T > + class const_component final { + public: + const_component(const const_component&) = default; + const_component& operator=(const const_component&) = default; + + const_component(const component& comp) noexcept; + const_component(const const_entity& owner) noexcept; + + const const_entity& owner() const noexcept; + + bool exists() const noexcept; + + const T& get() const; + const T* find() const noexcept; + private: + const_entity owner_; + }; + + template < typename T > + bool operator==(const const_component& l, const component& r) noexcept; + template < typename T > + bool operator==(const const_component& l, const const_component& r) noexcept; + + template < typename T > + bool operator!=(const const_component& l, const component& r) noexcept; + template < typename T > + bool operator!=(const const_component& l, const const_component& r) noexcept; +} + +namespace std +{ + template < typename T > + struct hash> + : std::unary_function&, std::size_t> + { + std::size_t operator()(const ecs_hpp::const_component& comp) const noexcept { + return std::hash()(comp.owner()); + } + }; +} + // ----------------------------------------------------------------------------- // // system @@ -855,6 +979,14 @@ namespace ecs_hpp registry() = default; ~registry() noexcept = default; + entity wrap_entity(const const_uentity& ent) noexcept; + const_entity wrap_entity(const const_uentity& ent) const noexcept; + + template < typename T > + component wrap_component(const const_uentity& ent) noexcept; + template < typename T > + const_component wrap_component(const const_uentity& ent) const noexcept; + entity create_entity(); bool destroy_entity(const uentity& ent); @@ -1091,9 +1223,18 @@ namespace ecs_hpp && l.id() == r.id(); } + inline bool operator==(const entity& l, const const_entity& r) noexcept { + return &l.owner() == &r.owner() + && l.id() == r.id(); + } + inline bool operator!=(const entity& l, const entity& r) noexcept { return !(l == r); } + + inline bool operator!=(const entity& l, const const_entity& r) noexcept { + return !(l == r); + } } // ----------------------------------------------------------------------------- @@ -1152,16 +1293,162 @@ namespace ecs_hpp return (*owner_).find_components(id_); } + inline bool operator==(const const_entity& l, const entity& r) noexcept { + return &l.owner() == &r.owner() + && l.id() == r.id(); + } + inline bool operator==(const const_entity& l, const const_entity& r) noexcept { return &l.owner() == &r.owner() && l.id() == r.id(); } + inline bool operator!=(const const_entity& l, const entity& r) noexcept { + return !(l == r); + } + inline bool operator!=(const const_entity& l, const const_entity& r) noexcept { return !(l == r); } } +// ----------------------------------------------------------------------------- +// +// component impl +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + template < typename T > + component::component(const entity& owner) noexcept + : owner_(owner) {} + + template < typename T > + entity& component::owner() noexcept { + return owner_; + } + + template < typename T > + const entity& component::owner() const noexcept { + return owner_; + } + + template < typename T > + bool component::remove() { + return owner_.remove_component(); + } + + template < typename T > + bool component::exists() const noexcept { + return owner_.exists_component(); + } + + template < typename T > + template < typename... Args > + bool component::assign(Args&&... args) { + return owner_.assign_component( + std::forward(args)...); + } + + template < typename T > + T& component::get() { + return owner_.get_component(); + } + + template < typename T > + const T& component::get() const { + return detail::as_const(owner_).template get_component(); + } + + template < typename T > + T* component::find() noexcept { + return owner_.find_component(); + } + + template < typename T > + const T* component::find() const noexcept { + return detail::as_const(owner_).template find_component(); + } + + template < typename T > + bool operator==(const component& l, const component& r) noexcept { + return l.owner() == r.owner(); + } + + template < typename T > + bool operator==(const component& l, const const_component& r) noexcept { + return l.owner() == r.owner(); + } + + template < typename T > + bool operator!=(const component& l, const component& r) noexcept { + return !(l == r); + } + + template < typename T > + bool operator!=(const component& l, const const_component& r) noexcept { + return !(l == r); + } +} + +// ----------------------------------------------------------------------------- +// +// const_component impl +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + template < typename T > + const_component::const_component(const component& comp) noexcept + : owner_(comp.owner()) {} + + template < typename T > + const_component::const_component(const const_entity& owner) noexcept + : owner_(owner) {} + + template < typename T > + const const_entity& const_component::owner() const noexcept { + return owner_; + } + + template < typename T > + bool const_component::exists() const noexcept { + return detail::as_const(owner_).template exists_component(); + } + + template < typename T > + const T& const_component::get() const { + return detail::as_const(owner_).template get_component(); + } + + template < typename T > + const T* const_component::find() const noexcept { + return detail::as_const(owner_).template find_component(); + } + + template < typename T > + bool operator==(const const_component& l, const component& r) noexcept { + return l.owner() == r.owner(); + } + + template < typename T > + bool operator==(const const_component& l, const const_component& r) noexcept { + return l.owner() == r.owner(); + } + + template < typename T > + bool operator!=(const const_component& l, const component& r) noexcept { + return !(l == r); + } + + template < typename T > + bool operator!=(const const_component& l, const const_component& r) noexcept { + return !(l == r); + } +} + // ----------------------------------------------------------------------------- // // registry impl @@ -1263,18 +1550,36 @@ namespace ecs_hpp // registry // + inline entity registry::wrap_entity(const const_uentity& ent) noexcept { + return {*this, ent.id()}; + } + + inline const_entity registry::wrap_entity(const const_uentity& ent) const noexcept { + return {*this, ent.id()}; + } + + template < typename T > + component registry::wrap_component(const const_uentity& ent) noexcept { + return {wrap_entity(ent)}; + } + + template < typename T > + const_component registry::wrap_component(const const_uentity& ent) const noexcept { + return {wrap_entity(ent)}; + } + inline entity registry::create_entity() { if ( !free_entity_ids_.empty() ) { const auto free_ent_id = free_entity_ids_.back(); const auto new_ent_id = detail::upgrade_entity_id(free_ent_id); entity_ids_.insert(new_ent_id); free_entity_ids_.pop_back(); - return {*this, new_ent_id}; + return wrap_entity(new_ent_id); } if ( last_entity_id_ < detail::entity_id_index_mask ) { entity_ids_.insert(last_entity_id_ + 1); - return {*this, ++last_entity_id_}; + return wrap_entity(++last_entity_id_); } throw std::logic_error("ecs_hpp::registry (entity index overlow)"); } diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 14ef415..9c3d0e2 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -285,15 +285,35 @@ TEST_CASE("detail") { TEST_CASE("registry") { SECTION("entities") { + { + ecs::registry w; + ecs::entity e1{w}; + ecs::entity e2 = e1; + e2 = e1; + + ecs::const_entity ce1{w}; + ecs::const_entity ce2 = ce1; + ce2 = ce1; + + ecs::const_entity ce3 = e1; + ce3 = e2; + } { ecs::registry w; ecs::entity e1{w}; ecs::entity e2{w}; + ecs::const_entity e3{w}; REQUIRE(e1 == e2); + REQUIRE(e2 == e3); + + REQUIRE_FALSE(e1 != e2); + REQUIRE_FALSE(e2 != e3); + REQUIRE_FALSE(w.alive_entity(e1)); REQUIRE_FALSE(w.alive_entity(e2)); + REQUIRE_FALSE(w.alive_entity(e3)); REQUIRE_FALSE(w.destroy_entity(e1)); REQUIRE_FALSE(w.destroy_entity(e2)); @@ -301,12 +321,23 @@ TEST_CASE("registry") { { ecs::registry w; - auto e1 = w.create_entity(); - auto e2 = w.create_entity(); + ecs::entity e1 = w.create_entity(); + ecs::entity e2 = w.create_entity(); + ecs::const_entity e3 = w.create_entity(); + ecs::entity ee3 = w.wrap_entity(e3); REQUIRE(e1 != e2); + REQUIRE(e2 != e3); + REQUIRE_FALSE(e3 != ee3); + + REQUIRE_FALSE(e1 == e2); + REQUIRE_FALSE(e2 == e3); + REQUIRE(e3 == ee3); + REQUIRE(w.alive_entity(e1)); REQUIRE(w.alive_entity(e2)); + REQUIRE(w.alive_entity(e3)); + REQUIRE(w.alive_entity(ee3)); REQUIRE(w.destroy_entity(e1)); REQUIRE_FALSE(w.alive_entity(e1)); @@ -316,8 +347,13 @@ TEST_CASE("registry") { REQUIRE_FALSE(w.alive_entity(e1)); REQUIRE_FALSE(w.alive_entity(e2)); + REQUIRE(w.destroy_entity(ee3)); + REQUIRE_FALSE(w.alive_entity(e3)); + REQUIRE_FALSE(w.alive_entity(ee3)); + REQUIRE_FALSE(w.destroy_entity(e1)); REQUIRE_FALSE(w.destroy_entity(e2)); + REQUIRE_FALSE(w.destroy_entity(ee3)); } { ecs::registry w; @@ -364,6 +400,92 @@ TEST_CASE("registry") { REQUIRE_THROWS_AS(w.create_entity(), std::logic_error); } } + SECTION("components") { + { + ecs::registry w; + ecs::entity e1 = w.create_entity(); + ecs::entity e2 = w.create_entity(); + + { + REQUIRE(w.wrap_component(e1) == ecs::component(e1)); + REQUIRE_FALSE(w.wrap_component(e1) == ecs::component(e2)); + } + { + const ecs::registry& ww = w; + REQUIRE(ww.wrap_component(e1) == ecs::component(e1)); + REQUIRE_FALSE(ww.wrap_component(e1) == ecs::component(e2)); + } + + { + ecs::component c1{e1}; + REQUIRE_FALSE(c1.exists()); + } + } + { + ecs::registry w; + ecs::const_entity e1 = w.create_entity(); + ecs::const_entity e2 = w.create_entity(); + + { + REQUIRE(w.wrap_component(e1) == ecs::const_component(e1)); + REQUIRE_FALSE(w.wrap_component(e1) == ecs::const_component(e2)); + } + } + { + ecs::registry w; + ecs::entity e1 = w.create_entity(); + + ecs::component c1 = w.wrap_component(e1); + ecs::const_component c2 = w.wrap_component(e1); + REQUIRE(c1 == c2); + REQUIRE_FALSE(c1 != c2); + + REQUIRE(c1.owner() == e1); + REQUIRE(c2.owner() == e1); + + REQUIRE_FALSE(c1.exists()); + REQUIRE_FALSE(c2.exists()); + REQUIRE_FALSE(c1.find()); + REQUIRE_FALSE(c2.find()); + REQUIRE_THROWS_AS(c1.get(), std::logic_error); + REQUIRE_THROWS_AS(c2.get(), std::logic_error); + + REQUIRE(c1.assign(4,2)); + + REQUIRE(c1.exists()); + REQUIRE(c2.exists()); + REQUIRE(c1.find()->x == 4); + REQUIRE(c1.find()->y == 2); + REQUIRE(c2.find()->x == 4); + REQUIRE(c2.find()->y == 2); + REQUIRE(c1.get().x == 4); + REQUIRE(c1.get().y == 2); + REQUIRE(c2.get().x == 4); + REQUIRE(c2.get().y == 2); + + REQUIRE(c1.assign(2,4)); + + REQUIRE(c1.find()->x == 2); + REQUIRE(c1.find()->y == 4); + REQUIRE(c2.find()->x == 2); + REQUIRE(c2.find()->y == 4); + REQUIRE(c1.get().x == 2); + REQUIRE(c1.get().y == 4); + REQUIRE(c2.get().x == 2); + REQUIRE(c2.get().y == 4); + + REQUIRE(c1.remove()); + + REQUIRE_FALSE(c1.exists()); + REQUIRE_FALSE(c2.exists()); + REQUIRE_FALSE(c1.find()); + REQUIRE_FALSE(c2.find()); + REQUIRE_THROWS_AS(c1.get(), std::logic_error); + REQUIRE_THROWS_AS(c2.get(), std::logic_error); + + REQUIRE_FALSE(c1.remove()); + } + } SECTION("component_assigning") { { ecs::registry w; @@ -418,6 +540,9 @@ TEST_CASE("registry") { REQUIRE_FALSE(e1.exists_component()); REQUIRE_FALSE(e1.exists_component()); + REQUIRE_FALSE(w.component_count()); + REQUIRE_FALSE(w.component_count()); + REQUIRE_FALSE(w.entity_component_count(e1)); } } {