Răsfoiți Sursa

added filtered views

Michele Caini 8 ani în urmă
părinte
comite
388175968d
3 a modificat fișierele cu 88 adăugiri și 11 ștergeri
  1. 21 3
      README.md
  2. 25 8
      src/registry.hpp
  3. 42 0
      test/registry.cpp

+ 21 - 3
README.md

@@ -73,6 +73,12 @@ int main() {
         else { ecs.destroy(entity); }
     }
 
+    std::cout << "filtered component view" << std::endl;
+
+    for(auto entity: ecs.view<Position>().exclude<Velocity>()) {
+        std::cout << (registry.has<Position>(entity)) << "/" << (registry.has<Velocity>(entity)) << std::endl;
+    }
+
     ecs.reset();
 }
 ```
@@ -189,12 +195,13 @@ Note that entities are numbers and nothing more. They are not classes and they h
 
 #### The View
 
-There are two different kinds of view, each one with a slighlty different interface:
+There are three different kinds of view, each one with a slighlty different interface:
 
 * The _single component view_.
 * The _multi component view_.
+* The _filtered view_.
 
-Both of them are iterable, that is both of them have `begin` and `end` member functions that are suitable for a range-based for loop:
+All of them are iterable. In other terms they have `begin` and `end` member functions that are suitable for a range-based for loop:
 
 ```
 auto view = registry.view<Position, Velocity>();
@@ -221,7 +228,18 @@ The multi component view has an additional member function:
 
 * `reset()`: reorganizes internal data so as to further create optimized iterators (use it whenever the data within the registry are known to be changed).
 
-Both the views can be used more than once. They return newly created and correctly initialized iterators whenever
+The filtered view is nothing more than a multi component view with an additional set of components that act as filters.<br/>
+Users can create filtered view either from a single component view or from a multi component view by means of the `exclude` member function:
+
+```
+auto view = registry.view<Position>().exclude<Velocity>();
+
+for(auto entity: view) {
+    // do whatever you want with your entities
+}
+```
+
+All the views can be used more than once. They return newly created and correctly initialized iterators whenever
 `begin` or `end` is invoked. Anyway views and iterators are tiny objects and the time to construct them can be safely ignored.
 I'd suggest not to store them anywhere and to invoke the `Registry::view` member function at each iteration to get a properly
 initialized view over which to iterate.

+ 25 - 8
src/registry.hpp

@@ -2,6 +2,7 @@
 #define ENTT_REGISTRY_HPP
 
 
+#include <tuple>
 #include <vector>
 #include <utility>
 #include <cstddef>
@@ -16,8 +17,8 @@ template<typename...>
 class View;
 
 
-template<template<typename...> class Pool, typename Entity, typename... Components, typename Type, typename... Types>
-class View<Pool<Entity, Components...>, Type, Types...> final {
+template<template<typename...> class Pool, typename Entity, typename... Components, typename Type, typename... Types, typename... Filters>
+class View<Pool<Entity, Components...>, std::tuple<Type, Types...>, std::tuple<Filters...>> final {
     using pool_type = Pool<Entity, Components...>;
     using entity_type = typename pool_type::entity_type;
 
@@ -25,9 +26,9 @@ class View<Pool<Entity, Components...>, Type, Types...> final {
         bool valid() const noexcept {
             using accumulator_type = bool[];
             bool check = pool.template has<Type>(entities[pos-1]);
-            accumulator_type accumulator = { true, (check = check && pool.template has<Types>(entities[pos-1]))... };
-            (void)accumulator;
-            return check;
+            accumulator_type types = { true, (check = check && pool.template has<Types>(entities[pos-1]))... };
+            accumulator_type filters = { true, (check = check && not pool.template has<Filters>(entities[pos-1]))... };
+            return void(types), void(filters), check;
         }
 
     public:
@@ -85,6 +86,9 @@ public:
     using iterator_type = ViewIterator;
     using size_type = typename pool_type::size_type;
 
+    template<typename... Comp>
+    using view_type = View<pool_type, std::tuple<Type, Types...>, std::tuple<Comp...>>;
+
     explicit View(pool_type &pool) noexcept
         : entities{pool.template entities<Type>()},
           size{pool.template size<Type>()},
@@ -95,6 +99,11 @@ public:
         (void)accumulator;
     }
 
+    template<typename... Comp>
+    view_type<Comp...> exclude() noexcept {
+        return view_type<Comp...>{pool};
+    }
+
     iterator_type begin() const noexcept {
         return ViewIterator{pool, entities, size};
     }
@@ -119,7 +128,7 @@ private:
 
 
 template<template<typename...> class Pool, typename Entity, typename... Components, typename Type>
-class View<Pool<Entity, Components...>, Type> final {
+class View<Pool<Entity, Components...>, std::tuple<Type>, std::tuple<>> final {
     using pool_type = Pool<Entity, Components...>;
     using entity_type = typename pool_type::entity_type;
 
@@ -165,8 +174,16 @@ public:
     using iterator_type = ViewIterator;
     using size_type = typename pool_type::size_type;
 
+    template<typename... Comp>
+    using view_type = View<pool_type, std::tuple<Type>, std::tuple<Comp...>>;
+
     explicit View(pool_type &pool) noexcept: pool{pool} {}
 
+    template<typename... Comp>
+    view_type<Comp...> exclude() noexcept {
+        return view_type<Comp...>{pool};
+    }
+
     iterator_type begin() const noexcept {
         return ViewIterator{pool.template entities<Type>(), pool.template size<Type>()};
     }
@@ -229,7 +246,7 @@ private:
 
 public:
     template<typename... Comp>
-    using view_type = View<pool_type, Comp...>;
+    using view_type = View<pool_type, std::tuple<Comp...>, std::tuple<>>;
 
     template<typename... Args>
     Registry(Args&&... args)
@@ -367,7 +384,7 @@ public:
     }
 
     template<typename... Comp>
-    view_type<Comp...> view() {
+    view_type<Comp...> view() noexcept {
         return view_type<Comp...>{pool};
     }
 

+ 42 - 0
test/registry.cpp

@@ -193,3 +193,45 @@ TEST(DefaultRegistry, EmptyViewMultipleComponent) {
 
     registry.reset();
 }
+
+TEST(DefaultRegistry, ViewSingleComponentWithExclude) {
+    using registry_type = entt::DefaultRegistry<int, char>;
+
+    registry_type registry;
+
+    registry_type::entity_type e1 = registry.create<char>();
+    registry_type::entity_type e2 = registry.create<int, char>();
+
+    auto view = registry.view<char>().exclude<int>();
+
+    ASSERT_NE(view.begin(), view.end());
+
+    ASSERT_EQ(*view.begin(), e1);
+    ASSERT_NE(*view.begin(), e2);
+    ASSERT_EQ(++view.begin(), view.end());
+    ASSERT_NO_THROW(registry.reset());
+
+    ASSERT_NO_THROW((registry.view<char>().exclude<int>().begin()++));
+    ASSERT_NO_THROW((++registry.view<char>().exclude<int>().begin()));
+}
+
+TEST(DefaultRegistry, ViewMultipleComponentWithExclude) {
+    using registry_type = entt::DefaultRegistry<int, char, double>;
+
+    registry_type registry;
+
+    registry_type::entity_type e1 = registry.create<int, char, double>();
+    registry_type::entity_type e2 = registry.create<char, double>();
+
+    auto view = registry.view<char, double>().exclude<int>();
+
+    ASSERT_NE(view.begin(), view.end());
+
+    ASSERT_NE(*view.begin(), e1);
+    ASSERT_EQ(*view.begin(), e2);
+    ASSERT_EQ(++view.begin(), view.end());
+    ASSERT_NO_THROW(registry.reset());
+
+    ASSERT_NO_THROW((registry.view<char>().exclude<int>().begin()++));
+    ASSERT_NO_THROW((++registry.view<char>().exclude<int>().begin()));
+}