瀏覽代碼

view supports exclusion list

Michele Caini 6 年之前
父節點
當前提交
90eeeedf52
共有 4 個文件被更改,包括 97 次插入52 次删除
  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?
 * nested groups: AB/ABC/ABCD/... (hints: sort, check functions)
   - 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>();
 ```
 
+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:
 
 ```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
 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
 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>
 class basic_view<Entity, exclude_t<Exclude...>, Component...> {
-    static_assert(sizeof...(Component) > 1);
-
     /*! @brief A registry is allowed to create views. */
     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 unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
-    using traits_type = entt_traits<std::underlying_type_t<Entity>>;
 
     class iterator {
         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()) {
                 ++(*this);
@@ -98,9 +96,8 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
 
         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:
@@ -138,14 +135,16 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
 
     private:
-        unchecked_type unchecked;
         underlying_iterator_type begin;
         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)
-    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 {
@@ -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 {
                 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>({}))...>) {
                         func(get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entity)...);
                     } else {
@@ -188,8 +187,8 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
                 }
             });
         } 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>({}))...>) {
                         func(std::get<pool_type<Type> *>(pools)->get(entity)...);
                     } else {
@@ -210,6 +209,9 @@ public:
 
     /**
      * @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.
      * @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 {
         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
      * 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.
      * @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 {
         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 {
         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 {
         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();
     }
 
@@ -366,7 +368,10 @@ public:
     decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
         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), ...);
         } else {
             return std::tuple<decltype(get<Comp>({}))...>{get<Comp>(entt)...};
@@ -500,6 +505,7 @@ public:
 
 private:
     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.
      * @return The component assigned to the entity.
      */
+    template<typename Comp = Component>
     decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT {
+        static_assert(std::is_same_v<Comp, Component>);
         ENTT_ASSERT(contains(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});
 
-    view.get(e0) = '1';
+    view.get<char>(e0) = '1';
     view.get(e1) = '2';
 
     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);
@@ -214,7 +214,7 @@ TEST(SingleComponentView, Less) {
     });
 }
 
-TEST(MultipleComponentView, Functionalities) {
+TEST(MultiComponentView, Functionalities) {
     entt::registry registry;
     auto view = registry.view<int, 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');
 }
 
-TEST(MultipleComponentView, Iterator) {
+TEST(MultiComponentView, Iterator) {
     entt::registry registry;
     const auto entity = registry.create();
     registry.assign<int>(entity);
@@ -290,7 +290,7 @@ TEST(MultipleComponentView, Iterator) {
     ASSERT_EQ(++view.begin(), view.end());
 }
 
-TEST(MultipleComponentView, Contains) {
+TEST(MultiComponentView, Contains) {
     entt::registry registry;
 
     const auto e0 = registry.create();
@@ -309,7 +309,7 @@ TEST(MultipleComponentView, Contains) {
     ASSERT_TRUE(view.contains(e1));
 }
 
-TEST(MultipleComponentView, Empty) {
+TEST(MultiComponentView, Empty) {
     entt::registry registry;
 
     const auto e0 = registry.create();
@@ -327,7 +327,7 @@ TEST(MultipleComponentView, Empty) {
     ASSERT_EQ(view.begin(), view.end());
 }
 
-TEST(MultipleComponentView, Each) {
+TEST(MultiComponentView, Each) {
     entt::registry registry;
 
     const auto e0 = registry.create();
@@ -353,7 +353,7 @@ TEST(MultipleComponentView, Each) {
     ASSERT_EQ(cnt, std::size_t{0});
 }
 
-TEST(MultipleComponentView, EachWithType) {
+TEST(MultiComponentView, EachWithType) {
     entt::registry registry;
 
     for(auto i = 0; i < 3; ++i) {
@@ -379,7 +379,7 @@ TEST(MultipleComponentView, EachWithType) {
     });
 }
 
-TEST(MultipleComponentView, EachWithHoles) {
+TEST(MultiComponentView, EachWithHoles) {
     entt::registry registry;
 
     const auto e0 = registry.create();
@@ -404,7 +404,7 @@ TEST(MultipleComponentView, EachWithHoles) {
     });
 }
 
-TEST(MultipleComponentView, ConstNonConstAndAllInBetween) {
+TEST(MultiComponentView, ConstNonConstAndAllInBetween) {
     entt::registry registry;
     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;
     auto view = registry.view<int, const char>();
 
@@ -476,6 +476,51 @@ TEST(MultipleComponentView, Find) {
     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) {
     entt::registry registry;
     const auto entity = std::get<0>(registry.create<int, char, double, entt::tag<"empty"_hs>>());