diff --git a/ecs.hpp b/ecs.hpp index 463da10..6f05bb5 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -277,25 +277,26 @@ namespace ecs_hpp template < typename T > T& get_component(const entity& ent); - template < typename T > const T& get_component(const entity& ent) const; template < typename T > T* find_component(const entity& ent) noexcept; - template < typename T > const T* find_component(const entity& ent) const noexcept; template < typename T, typename F > - void for_each_component(F&& f) noexcept; - + void for_each_component(F&& f); template < typename T, typename F > - void for_each_component(F&& f) const noexcept; + void for_each_component(F&& f) const; + + template < typename... Ts, typename F > + void for_joined_components(F&& f); + template < typename... Ts, typename F > + void for_joined_components(F&& f) const; private: template < typename T > detail::component_storage* find_storage_() noexcept; - template < typename T > const detail::component_storage* find_storage_() const noexcept; @@ -304,6 +305,25 @@ namespace ecs_hpp bool is_entity_alive_impl_(const entity& ent) const noexcept; std::size_t remove_all_components_impl_(const entity& ent) const noexcept; + + template < typename T > + T* find_component_impl_(const entity& ent) noexcept; + template < typename T > + const T* find_component_impl_(const entity& ent) const noexcept; + + template < typename... Ts, typename F > + void for_joined_components_impl_(F&& f); + template < typename T, typename... Ts, typename F, typename... Cs > + void for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs); + template < typename F, typename... Cs > + void for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs); + + template < typename... Ts, typename F > + void for_joined_components_impl_(F&& f) const; + template < typename T, typename... Ts, typename F, typename... Cs > + void for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const; + template < typename F, typename... Cs > + void for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const; private: mutable std::mutex mutex_; @@ -471,8 +491,7 @@ namespace ecs_hpp template < typename T > T& world::get_component(const entity& ent) { std::lock_guard guard(mutex_); - detail::component_storage* storage = find_storage_(); - T* component = storage ? storage->find(ent.id()) : nullptr; + T* component = find_component_impl_(ent); if ( component ) { return *component; } @@ -482,8 +501,7 @@ namespace ecs_hpp template < typename T > const T& world::get_component(const entity& ent) const { std::lock_guard guard(mutex_); - const detail::component_storage* storage = find_storage_(); - const T* component = storage ? storage->find(ent.id()) : nullptr; + const T* component = find_component_impl_(ent); if ( component ) { return *component; } @@ -493,23 +511,17 @@ namespace ecs_hpp template < typename T > T* world::find_component(const entity& ent) noexcept { std::lock_guard guard(mutex_); - detail::component_storage* storage = find_storage_(); - return storage - ? storage->find(ent.id()) - : nullptr; + return find_component_impl_(ent); } template < typename T > const T* world::find_component(const entity& ent) const noexcept { std::lock_guard guard(mutex_); - const detail::component_storage* storage = find_storage_(); - return storage - ? storage->find(ent.id()) - : nullptr; + return find_component_impl_(ent); } template < typename T, typename F > - void world::for_each_component(F&& f) noexcept { + void world::for_each_component(F&& f) { std::lock_guard guard(mutex_); detail::component_storage* storage = find_storage_(); if ( storage ) { @@ -518,7 +530,7 @@ namespace ecs_hpp } template < typename T, typename F > - void world::for_each_component(F&& f) const noexcept { + void world::for_each_component(F&& f) const { std::lock_guard guard(mutex_); const detail::component_storage* storage = find_storage_(); if ( storage ) { @@ -526,6 +538,18 @@ namespace ecs_hpp } } + template < typename... Ts, typename F > + void world::for_joined_components(F&& f) { + std::lock_guard guard(mutex_); + for_joined_components_impl_(std::forward(f)); + } + + template < typename... Ts, typename F > + void world::for_joined_components(F&& f) const { + std::lock_guard guard(mutex_); + for_joined_components_impl_(std::forward(f)); + } + template < typename T > detail::component_storage* world::find_storage_() noexcept { const auto family = detail::type_family::id(); @@ -577,4 +601,68 @@ namespace ecs_hpp } return removed_components; } + + template < typename T > + T* world::find_component_impl_(const entity& ent) noexcept { + detail::component_storage* storage = find_storage_(); + return storage + ? storage->find(ent.id()) + : nullptr; + } + + template < typename T > + const T* world::find_component_impl_(const entity& ent) const noexcept { + const detail::component_storage* storage = find_storage_(); + return storage + ? storage->find(ent.id()) + : nullptr; + } + + template < typename... Ts, typename F > + void world::for_joined_components_impl_(F&& f) { + for ( const auto& e : entities_ ) { + for_joined_components_impl_(e, std::forward(f)); + } + } + + template < typename T, typename... Ts, typename F, typename... Cs > + void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) { + T* c = find_component_impl_(e); + if ( c ) { + for_joined_components_impl_( + e, + std::forward(f), + std::forward(cs)..., + *c); + } + } + + template < typename F, typename... Cs > + void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) { + f(e, std::forward(cs)...); + } + + template < typename... Ts, typename F > + void world::for_joined_components_impl_(F&& f) const { + for ( const auto& e : entities_ ) { + for_joined_components_impl_(e, std::forward(f)); + } + } + + template < typename T, typename... Ts, typename F, typename... Cs > + void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const { + const T* c = find_component_impl_(e); + if ( c ) { + for_joined_components_impl_( + e, + std::forward(f), + std::forward(cs)..., + *c); + } + } + + template < typename F, typename... Cs > + void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const { + f(e, std::forward(cs)...); + } } diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 8f7d9dd..d4efab9 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -21,11 +21,11 @@ namespace }; struct velocity_c { - int dx{0}; - int dy{0}; + int x{0}; + int y{0}; velocity_c() = default; - velocity_c(int ndx, int ndy) : dx(ndx), dy(ndy) {} + velocity_c(int nx, int ny) : x(nx), y(ny) {} }; } @@ -163,8 +163,8 @@ TEST_CASE("world") { REQUIRE(e1.get_component().x == 1); REQUIRE(e1.get_component().y == 2); - REQUIRE(e2.get_component().dx == 3); - REQUIRE(e2.get_component().dy == 4); + REQUIRE(e2.get_component().x == 3); + REQUIRE(e2.get_component().y == 4); REQUIRE_THROWS_AS(e1.get_component(), ecs::basic_exception); REQUIRE_THROWS_AS(e2.get_component(), ecs::basic_exception); @@ -182,15 +182,15 @@ TEST_CASE("world") { w.assign_component(e2, 3, 4); REQUIRE(e1.find_component()->y == 2); - REQUIRE(e2.find_component()->dy == 4); + REQUIRE(e2.find_component()->y == 4); { const ecs::world& ww = w; REQUIRE(ww.get_component(e1).x == 1); REQUIRE(ww.get_component(e1).y == 2); - REQUIRE(ww.get_component(e2).dx == 3); - REQUIRE(ww.get_component(e2).dy == 4); + REQUIRE(ww.get_component(e2).x == 3); + REQUIRE(ww.get_component(e2).y == 4); REQUIRE_THROWS_AS(ww.get_component(e1), ecs::basic_exception); REQUIRE_THROWS_AS(ww.get_component(e2), ecs::basic_exception); @@ -239,4 +239,51 @@ TEST_CASE("world") { } } } + + SECTION("for_joined_components") { + { + ecs::world w; + + auto e1 = w.create_entity(); + auto e2 = w.create_entity(); + auto e3 = w.create_entity(); + auto e4 = w.create_entity(); + w.create_entity(); + + e1.assign_component(1, 2); + e1.assign_component(3, 4); + e2.assign_component(5, 6); + e2.assign_component(7, 8); + + e3.assign_component(100, 500); + e4.assign_component(500, 100); + + { + ecs::entity_id acc1 = 0; + int acc2 = 0; + w.for_joined_components([&acc1, &acc2]( + ecs::entity e, const position_c& p, const velocity_c& v) + { + acc1 += e.id(); + acc2 += p.x + v.x; + }); + REQUIRE(acc1 == e1.id() + e2.id()); + REQUIRE(acc2 == 16); + } + + { + const ecs::world& ww = w; + ecs::entity_id acc1 = 0; + int acc2 = 0; + ww.for_joined_components([&acc1, &acc2]( + ecs::entity e, const position_c& p, const velocity_c& v) + { + acc1 += e.id(); + acc2 += p.x + v.x; + }); + REQUIRE(acc1 == e1.id() + e2.id()); + REQUIRE(acc2 == 16); + } + } + } }