diff --git a/headers/ecs.hpp/ecs.hpp b/headers/ecs.hpp/ecs.hpp index f00160c..6476472 100644 --- a/headers/ecs.hpp/ecs.hpp +++ b/headers/ecs.hpp/ecs.hpp @@ -55,6 +55,10 @@ namespace ecs_hpp class option_conj; template < typename... Ts > class option_disj; + class option_bool; + + template < typename... Ts > + class aspect; class entity_filler; class registry_filler; @@ -1390,6 +1394,18 @@ namespace ecs_hpp template < typename T > detail::component_storage& get_or_create_storage_(); + template < typename F, typename... Opts > + void for_joined_components_impl_( + std::index_sequence<>, + F&& f, + Opts&&... opts); + + template < typename F, typename... Opts > + void for_joined_components_impl_( + std::index_sequence<>, + F&& f, + Opts&&... opts) const; + template < typename T , typename... Ts , typename F @@ -1508,6 +1524,11 @@ namespace ecs_hpp static constexpr bool instance = true; }; + template <> + struct option { + static constexpr bool instance = true; + }; + // // options // @@ -1583,6 +1604,19 @@ namespace ecs_hpp std::tuple opts_; }; + class option_bool final { + public: + option_bool(bool b) + : bool_(b) {} + + bool operator()(const const_entity& e) const { + (void)e; + return bool_; + } + private: + bool bool_{false}; + }; + // // operators // @@ -1608,6 +1642,57 @@ namespace ecs_hpp } } +// ----------------------------------------------------------------------------- +// +// aspect +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + template < typename... Ts > + class aspect { + public: + static auto to_option() noexcept { + return (option_bool{true} && ... && exists{}); + } + + static bool match_entity(const const_entity& e) noexcept { + return (... && e.exists_component()); + } + + template < typename F, typename... Opts > + static void for_each_entity(registry& owner, F&& f, Opts&&... opts) { + owner.for_joined_components( + [&f](const auto& e, const auto&...){ + f(e); + }, std::forward(opts)...); + } + + template < typename F, typename... Opts > + static void for_each_entity(const registry& owner, F&& f, Opts&&... opts) { + owner.for_joined_components( + [&f](const auto& e, const auto&...){ + f(e); + }, std::forward(opts)...); + } + + template < typename F, typename... Opts > + static void for_joined_components(registry& owner, F&& f, Opts&&... opts) { + owner.for_joined_components( + std::forward(f), + std::forward(opts)...); + } + + template < typename F, typename... Opts > + static void for_joined_components(const registry& owner, F&& f, Opts&&... opts) { + owner.for_joined_components( + std::forward(f), + std::forward(opts)...); + } + }; +} + // ----------------------------------------------------------------------------- // // fillers @@ -2683,6 +2768,24 @@ namespace ecs_hpp storages_.get(family).get()); } + template < typename F, typename... Opts > + void registry::for_joined_components_impl_( + std::index_sequence<>, + F&& f, + Opts&&... opts) + { + for_each_entity(std::forward(f), std::forward(opts)...); + } + + template < typename F, typename... Opts > + void registry::for_joined_components_impl_( + std::index_sequence<>, + F&& f, + Opts&&... opts) const + { + for_each_entity(std::forward(f), std::forward(opts)...); + } + template < typename T , typename... Ts , typename F diff --git a/untests/ecs_tests.cpp b/untests/ecs_tests.cpp index aaa943c..ec9addc 100644 --- a/untests/ecs_tests.cpp +++ b/untests/ecs_tests.cpp @@ -1185,6 +1185,102 @@ TEST_CASE("registry") { }); } } + SECTION("aspects") { + { + using empty_aspect = ecs::aspect<>; + + ecs::registry w; + + ecs::entity e1 = w.create_entity(); + REQUIRE(empty_aspect::match_entity(e1)); + + ecs::entity e2 = w.create_entity(); + e2.assign_component(); + e2.assign_component(1,2); + REQUIRE(empty_aspect::match_entity(e2)); + + ecs::entity e3 = w.create_entity(); + e3.assign_component(); + e3.assign_component(1,2); + e3.assign_component(3,4); + REQUIRE(empty_aspect::match_entity(e3)); + + { + ecs::entity_id acc{}; + empty_aspect::for_each_entity(w, [&acc](ecs::entity e){ + acc += e.id(); + }, ecs::exists{}); + REQUIRE(acc == e2.id() + e3.id()); + } + + { + ecs::entity_id acc{}; + empty_aspect::for_joined_components(w, [&acc](ecs::entity e){ + acc += e.id(); + }, ecs::exists{}); + REQUIRE(acc == e2.id() + e3.id()); + } + + { + ecs::entity_id acc{}; + w.for_each_entity([&acc](ecs::entity e){ + acc += e.id(); + }, empty_aspect::to_option()); + REQUIRE(acc == e1.id() + e2.id() + e3.id()); + } + } + { + using movable = ecs::aspect< + position_c, + velocity_c>; + + ecs::registry w; + + ecs::entity e = w.create_entity(); + REQUIRE_FALSE(movable::match_entity(e)); + + e.assign_component(1,2); + REQUIRE_FALSE(movable::match_entity(e)); + + e.assign_component(3,4); + REQUIRE(movable::match_entity(e)); + + ecs::entity e2 = w.create_entity(); + e2.assign_component(1,2); + + movable::for_joined_components(w, + [](ecs::entity_id, position_c& p, const velocity_c& v){ + p.x += v.x; + p.y += v.y; + }); + + movable::for_joined_components(std::as_const(w), + [](ecs::entity_id, const position_c& p, const velocity_c& v){ + const_cast(p).x += v.x; + const_cast(p).y += v.y; + }); + + w.for_each_entity([](ecs::entity e){ + auto& p = e.get_component(); + const auto& v = e.get_component(); + p.x += v.x; + p.y += v.y; + }, movable::to_option()); + + std::as_const(w).for_each_entity([](const ecs::const_entity& e){ + const auto& p = e.get_component(); + const auto& v = e.get_component(); + const_cast(p).x += v.x; + const_cast(p).y += v.y; + }, movable::to_option()); + + REQUIRE(e.get_component().x == 1 + 3*4); + REQUIRE(e.get_component().y == 2 + 4*4); + + REQUIRE(e2.get_component().x == 1); + REQUIRE(e2.get_component().y == 2); + } + } SECTION("options") { { ecs::registry w;