Просмотр исходного кода

added Registry::alive and Registry::orphans

Michele Caini 8 лет назад
Родитель
Сommit
a6cb0fc856
3 измененных файлов с 181 добавлено и 10 удалено
  1. 28 6
      README.md
  2. 68 4
      src/entt/entity/registry.hpp
  3. 85 0
      test/entt/entity/registry.cpp

+ 28 - 6
README.md

@@ -851,12 +851,15 @@ whether all the components have to be accessed or not.
 function template of a registry during iterations, if possible. However, keep in
 mind that it works only with the components of the view itself.
 
-### Give me everything
+### Give me everything, whatever it means
 
 Views are narrow windows on the entire list of entities. They work by filtering
 entities according to their components.<br/>
 In some cases there may be the need to iterate all the entities regardless of
-their components. The registry offers a specific member function to do that:
+their components. The registry offers a couple of member functions to do that.
+
+The former returns all the entities ever created, no matter if they are still in
+use or not:
 
 ```cpp
 registry.each([](auto entity) {
@@ -864,15 +867,34 @@ registry.each([](auto entity) {
 });
 ```
 
-Each entity ever created is returned, no matter if it's in use or not.<br/>
-Usually, filtering entities that aren't currently in use is more expensive than
-iterating them all and filtering out those in which one isn't interested.
+The latter returns an entity only if it's still in use (in other words, the
+entity is returned only if it hasn't been destroyed):
+
+```cpp
+registry.alive([](auto entity) {
+    // ...
+});
+```
 
 As a rule of thumb, consider using a view if the goal is to iterate entities
 that have a determinate set of components. A view is usually faster than
-combining this function with a bunch of custom tests.<br/>
+combining these functions with a bunch of custom tests.<br/>
 In all the other cases, this is the way to go.
 
+There exists also another function to use to retrieve orphans. An orphan is an
+entity that is in use and has no assigned components.<br/>
+The signature of the function is the same of `each` and `alive`:
+
+```cpp
+registry.orphans([](auto entity) {
+    // ...
+});
+```
+
+In general, `each` is pretty fast to run while `alive` is a bit slower because
+of the check it must perform on each and every entity. For similar reasons,
+`orphans` can be very slow and should not be used frequently.
+
 ## Side notes
 
 * Entity identifiers are numbers and nothing more. They are not classes and they

+ 68 - 4
src/entt/entity/registry.hpp

@@ -9,6 +9,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <cassert>
+#include <numeric>
 #include <algorithm>
 #include <type_traits>
 #include "../core/family.hpp"
@@ -401,7 +402,7 @@ public:
      * function can be used to know if they are still valid or the entity has
      * been destroyed and potentially recycled.
      *
-     * The returned entity has no components assigned.
+     * The returned entity has no assigned components.
      *
      * @return A valid entity identifier.
      */
@@ -922,7 +923,7 @@ public:
     }
 
     /**
-     * @brief Iterate entities and applies them the given function object.
+     * @brief Iterate all the entities ever created.
      *
      * The function object is invoked for each entity, no matter if it's in use
      * or not.<br/>
@@ -941,11 +942,74 @@ public:
      */
     template<typename Func>
     void each(Func func) const {
-        for(auto pos = entities.size(); pos > size_type{0}; --pos) {
-            func(entities[pos-1]);
+        for(size_type pos{}, last = entities.size(); pos < last; ++pos) {
+            func(entities[pos]);
         }
     }
 
+    /**
+     * @brief Iterate all the entities still in use.
+     *
+     * The function object is invoked for each entity that is still in use.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type);
+     * @endcode
+     *
+     * This function is fairly slow and should not be used frequently.<br/>
+     * Consider using a view if the goal is to iterate entities that have a
+     * determinate set of components. A view is usually faster than combining
+     * this function with a bunch of custom tests.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void alive(Func func) {
+        std::sort(available.begin(), available.end());
+
+        const auto end= available.cend();
+        auto it = available.cbegin();
+
+        each([func = std::move(func), it, end](auto entity) mutable {
+            if(it != end && *it == entity) {
+                ++it;
+            } else {
+                func(entity);
+            }
+        });
+    }
+
+    /**
+     * @brief Iterate orphans and applies them the given function object.
+     *
+     * The function object is invoked for each entity that is still in use and
+     * has no assigned components.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type);
+     * @endcode
+     *
+     * This function can be very slow and should not be used frequently.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void orphans(Func func) {
+        alive([func = std::move(func), this](auto entity) {
+            for(const auto &pool: pools) {
+                if(pool && pool->has(entity)) {
+                    return;
+                }
+            }
+
+            func(entity);
+        });
+    }
+
     /**
      * @brief Returns a standard view for the given components.
      *

+ 85 - 0
test/entt/entity/registry.cpp

@@ -173,6 +173,91 @@ TEST(DefaultRegistry, Each) {
     ASSERT_EQ(match, 0u);
 }
 
+TEST(DefaultRegistry, Alive) {
+    entt::DefaultRegistry registry;
+    entt::DefaultRegistry::size_type tot;
+    entt::DefaultRegistry::size_type match;
+
+    registry.create<int>();
+    registry.create<int>();
+
+    tot = 0u;
+    match = 0u;
+
+    registry.alive([&](auto entity) {
+        if(registry.has<int>(entity)) { ++match; }
+        registry.create();
+        ++tot;
+    });
+
+    ASSERT_EQ(tot, 2u);
+    ASSERT_EQ(match, 2u);
+
+    tot = 0u;
+    match = 0u;
+
+    registry.alive([&](auto entity) {
+        if(registry.has<int>(entity)) {
+            registry.destroy(entity);
+            ++match;
+        }
+
+        ++tot;
+    });
+
+    ASSERT_EQ(tot, 4u);
+    ASSERT_EQ(match, 2u);
+
+    tot = 0u;
+    match = 0u;
+
+    registry.alive([&](auto entity) {
+        if(registry.has<int>(entity)) { ++match; }
+        ++tot;
+    });
+
+    ASSERT_EQ(tot, 2u);
+    ASSERT_EQ(match, 0u);
+}
+
+TEST(DefaultRegistry, Orphans) {
+    entt::DefaultRegistry registry;
+    entt::DefaultRegistry::size_type tot;
+
+    registry.create<int>();
+    registry.create();
+    registry.create<int>();
+    registry.create();
+
+    tot = 0u;
+
+    registry.orphans([&](auto) {
+        ++tot;
+    });
+
+    ASSERT_EQ(tot, 2u);
+
+    registry.each([&](auto entity) {
+        registry.reset<int>(entity);
+    });
+
+    tot = 0u;
+
+    registry.orphans([&](auto) {
+        ++tot;
+    });
+
+    ASSERT_EQ(tot, 4u);
+
+    registry.reset();
+    tot = 0u;
+
+    registry.orphans([&](auto) {
+        ++tot;
+    });
+
+    ASSERT_EQ(tot, 0u);
+}
 
 TEST(DefaultRegistry, Types) {
     entt::DefaultRegistry registry;