Explorar el Código

view supports exclusion list

Michele Caini hace 6 años
padre
commit
90eeeedf52
Se han modificado 4 ficheros con 97 adiciones y 52 borrados
  1. 0 1
      TODO
  2. 8 15
      docs/md/entity.md
  3. 33 25
      src/entt/entity/view.hpp
  4. 56 11
      test/entt/entity/view.cpp

+ 0 - 1
TODO

@@ -34,4 +34,3 @@ TODO
 * range based registry::remove and some others?
 * range based registry::remove and some others?
 * nested groups: AB/ABC/ABCD/... (hints: sort, check functions)
 * nested groups: AB/ABC/ABCD/... (hints: sort, check functions)
   - sink::before and ordered calls
   - sink::before and ordered calls
-* view and exclusion list

+ 8 - 15
docs/md/entity.md

@@ -1107,6 +1107,12 @@ auto single = registry.view<position>();
 auto multi = registry.view<position, velocity>();
 auto multi = registry.view<position, velocity>();
 ```
 ```
 
 
+Filtering entities by components is also supported:
+
+```cpp
+auto view = registry.view<position, velocity>(entt::exclude<renderable>);
+```
+
 To iterate a view, either use it in a range-for loop:
 To iterate a view, either use it in a range-for loop:
 
 
 ```cpp
 ```cpp
@@ -1141,21 +1147,8 @@ There exists also an alternative version of `each` named `less` that works
 exactly as its counterpart but for the fact that it doesn't return empty
 exactly as its counterpart but for the fact that it doesn't return empty
 components to the caller.
 components to the caller.
 
 
-As a side note, when using a single component view, the most common error is to
-invoke `get` with the type of the component as a template parameter. This is
-probably due to the fact that it's required for multi component views:
-
-```cpp
-auto view = registry.view<position, const velocity>();
-
-for(auto entity: view) {
-    const auto &vel = view.get<const velocity>(entity);
-    // ...
-}
-```
-
-However, in case of a single component view, `get` doesn't accept a template
-parameter, since it's implicitly defined by the view itself:
+As a side note, in the case of single component views, `get` accepts but doesn't
+strictly require a template parameter, since the type is implicitly defined:
 
 
 ```cpp
 ```cpp
 auto view = registry.view<const renderable>();
 auto view = registry.view<const renderable>();

+ 33 - 25
src/entt/entity/view.hpp

@@ -69,8 +69,6 @@ class basic_view;
  */
  */
 template<typename Entity, typename... Exclude, typename... Component>
 template<typename Entity, typename... Exclude, typename... Component>
 class basic_view<Entity, exclude_t<Exclude...>, Component...> {
 class basic_view<Entity, exclude_t<Exclude...>, Component...> {
-    static_assert(sizeof...(Component) > 1);
-
     /*! @brief A registry is allowed to create views. */
     /*! @brief A registry is allowed to create views. */
     friend class basic_registry<Entity>;
     friend class basic_registry<Entity>;
 
 
@@ -82,15 +80,15 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
 
 
     using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
     using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
     using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
     using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
-    using traits_type = entt_traits<std::underlying_type_t<Entity>>;
 
 
     class iterator {
     class iterator {
         friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
         friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
 
 
-        iterator(unchecked_type other, underlying_iterator_type first, underlying_iterator_type last) ENTT_NOEXCEPT
-            : unchecked{other},
-              begin{first},
-              end{last}
+        iterator(underlying_iterator_type first, underlying_iterator_type last, unchecked_type other, std::tuple<pool_type<Exclude> *...> exclude) ENTT_NOEXCEPT
+            : begin{first},
+              end{last},
+              unchecked{other},
+              filter{exclude}
         {
         {
             if(begin != end && !valid()) {
             if(begin != end && !valid()) {
                 ++(*this);
                 ++(*this);
@@ -98,9 +96,8 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
         }
 
 
         bool valid() const ENTT_NOEXCEPT {
         bool valid() const ENTT_NOEXCEPT {
-            return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set<Entity> *view) {
-                return view->has(*begin);
-            });
+            return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); })
+                    && (!std::get<pool_type<Exclude> *>(filter)->has(*begin) && ...);
         }
         }
 
 
     public:
     public:
@@ -138,14 +135,16 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
         }
 
 
     private:
     private:
-        unchecked_type unchecked;
         underlying_iterator_type begin;
         underlying_iterator_type begin;
         underlying_iterator_type end;
         underlying_iterator_type end;
+        unchecked_type unchecked;
+        std::tuple<pool_type<Exclude> *...> filter;
     };
     };
 
 
     // we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
     // we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
-    basic_view(storage<Entity, std::remove_const_t<Component>> *... ref) ENTT_NOEXCEPT
-        : pools{ref...}
+    basic_view(storage<Entity, std::remove_const_t<Component>> *... component, storage<Entity, std::remove_const_t<Exclude>> *... exclude) ENTT_NOEXCEPT
+        : pools{component...},
+          filter{exclude...}
     {}
     {}
 
 
     const sparse_set<Entity> * candidate() const ENTT_NOEXCEPT {
     const sparse_set<Entity> * candidate() const ENTT_NOEXCEPT {
@@ -179,7 +178,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
             std::for_each(begin, end, [this, raw = std::get<pool_type<Comp> *>(pools)->begin(), &func](const auto entity) mutable {
             std::for_each(begin, end, [this, raw = std::get<pool_type<Comp> *>(pools)->begin(), &func](const auto entity) mutable {
                 auto curr = raw++;
                 auto curr = raw++;
 
 
-                if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
+                if((std::get<pool_type<Other> *>(pools)->has(entity) && ...) && (!std::get<pool_type<Exclude> *>(filter)->has(entity) && ...)) {
                     if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
                     if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
                         func(get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entity)...);
                         func(get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entity)...);
                     } else {
                     } else {
@@ -188,8 +187,8 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
                 }
                 }
             });
             });
         } else {
         } else {
-            std::for_each(begin, end, [this, &func](const auto entity) mutable {
-                if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
+            std::for_each(begin, end, [this, &func](const auto entity) {
+                if((std::get<pool_type<Other> *>(pools)->has(entity) && ...) && (!std::get<pool_type<Exclude> *>(filter)->has(entity) && ...)) {
                     if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
                     if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
                         func(std::get<pool_type<Type> *>(pools)->get(entity)...);
                         func(std::get<pool_type<Type> *>(pools)->get(entity)...);
                     } else {
                     } else {
@@ -210,6 +209,9 @@ public:
 
 
     /**
     /**
      * @brief Returns the number of existing components of the given type.
      * @brief Returns the number of existing components of the given type.
+     *
+     * This isn't the number of entities iterated by the view.
+     *
      * @tparam Comp Type of component of which to return the size.
      * @tparam Comp Type of component of which to return the size.
      * @return Number of existing components of the given type.
      * @return Number of existing components of the given type.
      */
      */
@@ -219,8 +221,8 @@ public:
     }
     }
 
 
     /**
     /**
-     * @brief Estimates the number of entities that have the given components.
-     * @return Estimated number of entities that have the given components.
+     * @brief Estimates the number of entities iterated by the view.
+     * @return Estimated number of entities iterated by the view.
      */
      */
     size_type size() const ENTT_NOEXCEPT {
     size_type size() const ENTT_NOEXCEPT {
         return std::min({ std::get<pool_type<Component> *>(pools)->size()... });
         return std::min({ std::get<pool_type<Component> *>(pools)->size()... });
@@ -230,9 +232,9 @@ public:
      * @brief Checks whether the view or the pools of the given components are
      * @brief Checks whether the view or the pools of the given components are
      * empty.
      * empty.
      *
      *
-     * The view is definitely empty if one of the pools is empty. In all other
-     * cases, the view may be empty and not return entities even if this
-     * function returns false.
+     * The view is definitely empty if one of the pools of the given components
+     * is empty. In all other cases, the view may be empty and not return
+     * entities even if this function returns false.
      *
      *
      * @tparam Comp Types of components in which one is interested.
      * @tparam Comp Types of components in which one is interested.
      * @return True if the view or the pools of the given components are empty,
      * @return True if the view or the pools of the given components are empty,
@@ -301,7 +303,7 @@ public:
      */
      */
     iterator_type begin() const ENTT_NOEXCEPT {
     iterator_type begin() const ENTT_NOEXCEPT {
         const auto *view = candidate();
         const auto *view = candidate();
-        return iterator_type{unchecked(view), view->begin(), view->end()};
+        return iterator_type{view->begin(), view->end(), unchecked(view), filter};
     }
     }
 
 
     /**
     /**
@@ -321,7 +323,7 @@ public:
      */
      */
     iterator_type end() const ENTT_NOEXCEPT {
     iterator_type end() const ENTT_NOEXCEPT {
         const auto *view = candidate();
         const auto *view = candidate();
-        return iterator_type{unchecked(view), view->end(), view->end()};
+        return iterator_type{view->end(), view->end(), unchecked(view), filter};
     }
     }
 
 
     /**
     /**
@@ -332,7 +334,7 @@ public:
      */
      */
     iterator_type find(const entity_type entt) const ENTT_NOEXCEPT {
     iterator_type find(const entity_type entt) const ENTT_NOEXCEPT {
         const auto *view = candidate();
         const auto *view = candidate();
-        iterator_type it{unchecked(view), view->find(entt), view->end()};
+        iterator_type it{view->find(entt), view->end(), unchecked(view), filter};
         return (it != end() && *it == entt) ? it : end();
         return (it != end() && *it == entt) ? it : end();
     }
     }
 
 
@@ -366,7 +368,10 @@ public:
     decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
     decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
         ENTT_ASSERT(contains(entt));
         ENTT_ASSERT(contains(entt));
 
 
-        if constexpr(sizeof...(Comp) == 1) {
+        if constexpr(sizeof...(Comp) == 0) {
+            static_assert(sizeof...(Component) == 1);
+            return (std::get<pool_type<Component> *>(pools)->get(entt), ...);
+        } else if constexpr(sizeof...(Comp) == 1) {
             return (std::get<pool_type<Comp> *>(pools)->get(entt), ...);
             return (std::get<pool_type<Comp> *>(pools)->get(entt), ...);
         } else {
         } else {
             return std::tuple<decltype(get<Comp>({}))...>{get<Comp>(entt)...};
             return std::tuple<decltype(get<Comp>({}))...>{get<Comp>(entt)...};
@@ -500,6 +505,7 @@ public:
 
 
 private:
 private:
     const std::tuple<pool_type<Component> *...> pools;
     const std::tuple<pool_type<Component> *...> pools;
+    const std::tuple<pool_type<Exclude> *...> filter;
 };
 };
 
 
 
 
@@ -686,7 +692,9 @@ public:
      * @param entt A valid entity identifier.
      * @param entt A valid entity identifier.
      * @return The component assigned to the entity.
      * @return The component assigned to the entity.
      */
      */
+    template<typename Comp = Component>
     decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT {
     decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT {
+        static_assert(std::is_same_v<Comp, Component>);
         ENTT_ASSERT(contains(entt));
         ENTT_ASSERT(contains(entt));
         return pool->get(entt);
         return pool->get(entt);
     }
     }

+ 56 - 11
test/entt/entity/view.cpp

@@ -30,11 +30,11 @@ TEST(SingleComponentView, Functionalities) {
 
 
     ASSERT_EQ(view.size(), typename decltype(view)::size_type{2});
     ASSERT_EQ(view.size(), typename decltype(view)::size_type{2});
 
 
-    view.get(e0) = '1';
+    view.get<char>(e0) = '1';
     view.get(e1) = '2';
     view.get(e1) = '2';
 
 
     for(auto entity: view) {
     for(auto entity: view) {
-        ASSERT_TRUE(cview.get(entity) == '1' || cview.get(entity) == '2');
+        ASSERT_TRUE(cview.get<const char>(entity) == '1' || cview.get(entity) == '2');
     }
     }
 
 
     ASSERT_EQ(*(view.data() + 0), e1);
     ASSERT_EQ(*(view.data() + 0), e1);
@@ -214,7 +214,7 @@ TEST(SingleComponentView, Less) {
     });
     });
 }
 }
 
 
-TEST(MultipleComponentView, Functionalities) {
+TEST(MultiComponentView, Functionalities) {
     entt::registry registry;
     entt::registry registry;
     auto view = registry.view<int, char>();
     auto view = registry.view<int, char>();
     auto cview = std::as_const(registry).view<const int, const char>();
     auto cview = std::as_const(registry).view<const int, const char>();
@@ -268,7 +268,7 @@ TEST(MultipleComponentView, Functionalities) {
     ASSERT_EQ(*(cview.raw<const char>() + 1), '2');
     ASSERT_EQ(*(cview.raw<const char>() + 1), '2');
 }
 }
 
 
-TEST(MultipleComponentView, Iterator) {
+TEST(MultiComponentView, Iterator) {
     entt::registry registry;
     entt::registry registry;
     const auto entity = registry.create();
     const auto entity = registry.create();
     registry.assign<int>(entity);
     registry.assign<int>(entity);
@@ -290,7 +290,7 @@ TEST(MultipleComponentView, Iterator) {
     ASSERT_EQ(++view.begin(), view.end());
     ASSERT_EQ(++view.begin(), view.end());
 }
 }
 
 
-TEST(MultipleComponentView, Contains) {
+TEST(MultiComponentView, Contains) {
     entt::registry registry;
     entt::registry registry;
 
 
     const auto e0 = registry.create();
     const auto e0 = registry.create();
@@ -309,7 +309,7 @@ TEST(MultipleComponentView, Contains) {
     ASSERT_TRUE(view.contains(e1));
     ASSERT_TRUE(view.contains(e1));
 }
 }
 
 
-TEST(MultipleComponentView, Empty) {
+TEST(MultiComponentView, Empty) {
     entt::registry registry;
     entt::registry registry;
 
 
     const auto e0 = registry.create();
     const auto e0 = registry.create();
@@ -327,7 +327,7 @@ TEST(MultipleComponentView, Empty) {
     ASSERT_EQ(view.begin(), view.end());
     ASSERT_EQ(view.begin(), view.end());
 }
 }
 
 
-TEST(MultipleComponentView, Each) {
+TEST(MultiComponentView, Each) {
     entt::registry registry;
     entt::registry registry;
 
 
     const auto e0 = registry.create();
     const auto e0 = registry.create();
@@ -353,7 +353,7 @@ TEST(MultipleComponentView, Each) {
     ASSERT_EQ(cnt, std::size_t{0});
     ASSERT_EQ(cnt, std::size_t{0});
 }
 }
 
 
-TEST(MultipleComponentView, EachWithType) {
+TEST(MultiComponentView, EachWithType) {
     entt::registry registry;
     entt::registry registry;
 
 
     for(auto i = 0; i < 3; ++i) {
     for(auto i = 0; i < 3; ++i) {
@@ -379,7 +379,7 @@ TEST(MultipleComponentView, EachWithType) {
     });
     });
 }
 }
 
 
-TEST(MultipleComponentView, EachWithHoles) {
+TEST(MultiComponentView, EachWithHoles) {
     entt::registry registry;
     entt::registry registry;
 
 
     const auto e0 = registry.create();
     const auto e0 = registry.create();
@@ -404,7 +404,7 @@ TEST(MultipleComponentView, EachWithHoles) {
     });
     });
 }
 }
 
 
-TEST(MultipleComponentView, ConstNonConstAndAllInBetween) {
+TEST(MultiComponentView, ConstNonConstAndAllInBetween) {
     entt::registry registry;
     entt::registry registry;
     auto view = registry.view<int, const char, std::true_type>();
     auto view = registry.view<int, const char, std::true_type>();
 
 
@@ -431,7 +431,7 @@ TEST(MultipleComponentView, ConstNonConstAndAllInBetween) {
     });
     });
 }
 }
 
 
-TEST(MultipleComponentView, Find) {
+TEST(MultiComponentView, Find) {
     entt::registry registry;
     entt::registry registry;
     auto view = registry.view<int, const char>();
     auto view = registry.view<int, const char>();
 
 
@@ -476,6 +476,51 @@ TEST(MultipleComponentView, Find) {
     ASSERT_EQ(view.find(e4), view.end());
     ASSERT_EQ(view.find(e4), view.end());
 }
 }
 
 
+TEST(MultiComponentView, ExcludedComponents) {
+    entt::registry registry;
+
+    const auto e0 = registry.create();
+    registry.assign<int>(e0, 0);
+
+    const auto e1 = registry.create();
+    registry.assign<int>(e1, 1);
+    registry.assign<char>(e1);
+
+    const auto view = registry.view<int>(entt::exclude<char>);
+
+    const auto e2 = registry.create();
+    registry.assign<int>(e2, 2);
+
+    const auto e3 = registry.create();
+    registry.assign<int>(e3, 3);
+    registry.assign<char>(e3);
+
+    for(const auto entity: view) {
+        if(entity == e0) {
+            ASSERT_EQ(view.get<int>(e0), 0);
+        } else if(entity == e2) {
+            ASSERT_EQ(view.get(e2), 2);
+        } else {
+            FAIL();
+        }
+    }
+
+    registry.assign<char>(e0);
+    registry.assign<char>(e2);
+    registry.remove<char>(e1);
+    registry.remove<char>(e3);
+
+    for(const auto entity: view) {
+        if(entity == e1) {
+            ASSERT_EQ(view.get(e1), 1);
+        } else if(entity == e3) {
+            ASSERT_EQ(view.get<int>(e3), 3);
+        } else {
+            FAIL();
+        }
+    }
+}
+
 TEST(MultiComponentView, Less) {
 TEST(MultiComponentView, Less) {
     entt::registry registry;
     entt::registry registry;
     const auto entity = std::get<0>(registry.create<int, char, double, entt::tag<"empty"_hs>>());
     const auto entity = std::get<0>(registry.create<int, char, double, entt::tag<"empty"_hs>>());