浏览代码

updated entity-component system

Michele Caini 8 年之前
父节点
当前提交
a06c891969

+ 67 - 35
src/entt/entity/registry.hpp

@@ -17,7 +17,7 @@ namespace entt {
 
 
 /**
- * @brief A repository class for entities and components.
+ * @brief Fast and reliable entity-component system.
  *
  * The registry is the core class of the entity-component framework.<br/>
  * It stores entities and arranges pools of components on a per request basis.
@@ -61,7 +61,7 @@ class Registry {
             }
         }
 
-        void append(SparseSet<Entity> &handler, test_fn_type fn) {
+        inline void append(SparseSet<Entity> &handler, test_fn_type fn) {
             listeners.emplace_back(handler, fn);
         }
 
@@ -115,20 +115,20 @@ public:
     /*! @brief Default destructor. */
     ~Registry() = default;
 
-    /*! @brief Copying a sparse set isn't allowed. */
+    /*! @brief Copying a registry isn't allowed. */
     Registry(const Registry &) = delete;
-    /*! @brief Moving a sparse set isn't allowed. */
+    /*! @brief Moving a registry isn't allowed. */
     Registry(Registry &&) = delete;
 
-    /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */
+    /*! @brief Copying a registry isn't allowed. @return This registry. */
     Registry & operator=(const Registry &) = delete;
-    /*! @brief Moving a sparse set isn't allowed. @return This sparse set. */
+    /*! @brief Moving a registry isn't allowed. @return This registry. */
     Registry & operator=(Registry &&) = delete;
 
     /**
      * @brief Returns the number of existing components of the given type.
-     * @tparam Component The type of the component to which to return the size.
-     * @return The number of existing components of the given type.
+     * @tparam Component Type of component of which to return the size.
+     * @return Number of existing components of the given type.
      */
     template<typename Component>
     size_type size() const noexcept {
@@ -137,7 +137,7 @@ public:
 
     /**
      * @brief Returns the number of entities still in use.
-     * @return The number of entities still in use.
+     * @return Number of entities still in use.
      */
     size_type size() const noexcept {
         return entities.size() - available.size();
@@ -145,7 +145,7 @@ public:
 
     /**
      * @brief Returns the number of entities ever created.
-     * @return The number of entities ever created.
+     * @return Number of entities ever created.
      */
     size_type capacity() const noexcept {
         return entities.size();
@@ -153,7 +153,7 @@ public:
 
     /**
      * @brief Checks whether the pool for the given component is empty.
-     * @tparam Component The type of the component in which one is interested.
+     * @tparam Component Type of component in which one is interested.
      * @return True if the pool for the given component is empty, false
      * otherwise.
      */
@@ -183,7 +183,7 @@ public:
     /**
      * @brief Returns the version stored along with the given entity identifier.
      * @param entity An entity identifier, either valid or not.
-     * @return The version stored along with the given entity identifier.
+     * @return Version stored along with the given entity identifier.
      */
     version_type version(entity_type entity) const noexcept {
         return version_type((entity >> traits_type::version_shift) & traits_type::version_mask);
@@ -204,7 +204,7 @@ public:
      * registry doesn't own the given entity.
      *
      * @param entity A valid entity identifier.
-     * @return The actual version for the given entity identifier.
+     * @return Actual version for the given entity identifier.
      */
     version_type current(entity_type entity) const noexcept {
         const auto entt = entity & traits_type::entity_mask;
@@ -212,6 +212,33 @@ public:
         return version_type((entities[entt] >> traits_type::version_shift) & traits_type::version_mask);
     }
 
+    /**
+     * @brief Returns a new entity initialized with the given components.
+     *
+     * There are two kinds of entity identifiers:
+     * * Newly created ones in case no entities have been previously destroyed.
+     * * Recycled one with updated versions.
+     *
+     * Users should not care about the type of the returned entity identifier.
+     * In case entity identifers are stored around, the `current` member
+     * function can be used to know if they are still valid or the entity has
+     * been destroyed and potentially recycled.
+     *
+     * The returned entity has fully initialized components assigned.
+     *
+     * @tparam Component A list of components to assign to the entity.
+     * @param components Instances with which to initialize components.
+     * @return A valid entity identifier.
+     */
+    template<typename... Component>
+    entity_type create(Component&&... components) noexcept {
+        using accumulator_type = int[];
+        const auto entity = create();
+        accumulator_type accumulator = { 0, (ensure<Component>().construct(*this, entity, std::forward<Component>(components)), 0)... };
+        (void)accumulator;
+        return entity;
+    }
+
     /**
      * @brief Returns a new entity to which the given components are assigned.
      *
@@ -224,6 +251,8 @@ 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 default initialized components assigned.
+     *
      * @tparam Component A list of components to assign to the entity.
      * @return A valid entity identifier.
      */
@@ -248,6 +277,8 @@ 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.
+     *
      * @return A valid entity identifier.
      */
     entity_type create() noexcept {
@@ -255,6 +286,7 @@ public:
 
         if(available.empty()) {
             entity = entity_type(entities.size());
+            assert(entity < traits_type::entity_mask);
             assert((entity >> traits_type::version_shift) == entity_type{});
             entities.push_back(entity);
         } else {
@@ -310,10 +342,10 @@ public:
      * invalid entity or if the entity already owns an instance of the given
      * component.
      *
-     * @tparam Component The type of the component to create.
-     * @tparam Args The types of the arguments used to construct the component.
+     * @tparam Component Type of the component to create.
+     * @tparam Args Types of arguments to use to construct the component.
      * @param entity A valid entity identifier.
-     * @param args The parameters to use to initialize the component.
+     * @param args Parameters to use to initialize the component.
      * @return A reference to the newly created component.
      */
     template<typename Component, typename... Args>
@@ -332,7 +364,7 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      *
-     * @tparam Component The type of the component to remove.
+     * @tparam Component Type of the component to remove.
      * @param entity A valid entity identifier.
      */
     template<typename Component>
@@ -349,7 +381,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      *
-     * @tparam Component The components for which to perform the check.
+     * @tparam Component Components for which to perform the check.
      * @param entity A valid entity identifier.
      * @return True if the entity has all the components, false otherwise.
      */
@@ -374,7 +406,7 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      *
-     * @tparam Component The type of the component to get.
+     * @tparam Component Type of the component to get.
      * @param entity A valid entity identifier.
      * @return A reference to the instance of the component owned by the entity.
      */
@@ -394,7 +426,7 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      *
-     * @tparam Component The type of the component to get.
+     * @tparam Component Type of the component to get.
      * @param entity A valid entity identifier.
      * @return A reference to the instance of the component owned by the entity.
      */
@@ -417,10 +449,10 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      *
-     * @tparam Component The type of the component to replace.
-     * @tparam Args The types of the arguments used to construct the component.
+     * @tparam Component Type of the component to replace.
+     * @tparam Args Types of arguments to use to construct the component.
      * @param entity A valid entity identifier.
-     * @param args The parameters to use to initialize the component.
+     * @param args Parameters to use to initialize the component.
      * @return A reference to the newly created component.
      */
     template<typename Component, typename... Args>
@@ -450,10 +482,10 @@ public:
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      *
-     * @tparam Component The type of the component to assign or replace.
-     * @tparam Args The types of the arguments used to construct the component.
+     * @tparam Component Type of the component to assign or replace.
+     * @tparam Args Types of arguments to use to construct the component.
      * @param entity A valid entity identifier.
-     * @param args The parameters to use to initialize the component.
+     * @param args Parameters to use to initialize the component.
      * @return A reference to the newly created component.
      */
     template<typename Component, typename... Args>
@@ -487,8 +519,8 @@ public:
      *
      * Where `e1` and `e2` are valid entity identifiers.
      *
-     * @tparam Component The type of the components to sort.
-     * @tparam Compare The type of the comparison function object.
+     * @tparam Component Type of the components to sort.
+     * @tparam Compare Type of the comparison function object.
      * @param compare A valid comparison function object.
      */
     template<typename Component, typename Compare>
@@ -527,8 +559,8 @@ public:
      *
      * Any subsequent change to `B` won't affect the order in `A`.
      *
-     * @tparam To The type of the components to sort.
-     * @tparam From The type of the components to use to sort.
+     * @tparam To Type of the components to sort.
+     * @tparam From Type of the components to use to sort.
      */
     template<typename To, typename From>
     void sort() {
@@ -546,7 +578,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      *
-     * @tparam Component The component to reset.
+     * @tparam Component Type of the component to reset.
      * @param entity A valid entity identifier.
      */
     template<typename Component>
@@ -568,7 +600,7 @@ public:
      * For each entity that has an instance of the given component, the
      * component itself is removed and thus destroyed.
      *
-     * @tparam Component The component whose pool must be reset.
+     * @tparam Component type of the component whose pool must be reset.
      */
     template<typename Component>
     void reset() {
@@ -630,7 +662,7 @@ public:
      * @see View<Entity, Component>
      * @see PersistentView
      *
-     * @tparam Component The type of the components used to construct the view.
+     * @tparam Component Type of the components used to construct the view.
      * @return A newly created standard view.
      */
     template<typename... Component>
@@ -652,7 +684,7 @@ public:
      * can be prepared with this function. Just use the same set of components
      * that would have been used otherwise to contruct the view.
      *
-     * @tparam Component The types of the components used to prepare the view.
+     * @tparam Component Types of the components used to prepare the view.
      */
     template<typename... Component>
     void prepare() {
@@ -714,7 +746,7 @@ public:
      * @see View<Entity, Component>
      * @see PersistentView
      *
-     * @tparam Component The types of the components used to construct the view.
+     * @tparam Component Types of the components used to construct the view.
      * @return A newly created persistent view.
      */
     template<typename... Component>

+ 7 - 7
src/entt/entity/sparse_set.hpp

@@ -110,7 +110,7 @@ public:
 
     /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */
     SparseSet & operator=(const SparseSet &) = delete;
-    /*! @brief Default move operator. @return This sparse set. */
+    /*! @brief Default move assignment operator. @return This sparse set. */
     SparseSet & operator=(SparseSet &&) = default;
 
     /**
@@ -121,7 +121,7 @@ public:
      * Usually the size of the internal sparse array is equal or greater than
      * the one of the internal packed array.
      *
-     * @return The number of elements.
+     * @return Number of elements.
      */
     size_type size() const noexcept {
         return direct.size();
@@ -297,7 +297,7 @@ public:
      * Attempting to iterate elements using the raw pointer returned by `data`
      * gives no guarantees on the order, even though `sort` has been invoked.
      *
-     * @tparam Compare The type of the comparison function.
+     * @tparam Compare Type of the comparison function.
      * @param compare A comparison function whose signature shall be equivalent
      * to: `bool(Entity, Entity)`.
      */
@@ -396,7 +396,7 @@ private:
  * @sa SparseSet<Entity>
  *
  * @tparam Entity A valid entity type (see entt_traits for more details).
- * @tparam Type The type of the objects assigned to the entities.
+ * @tparam Type Type of objects assigned to the entities.
  */
 template<typename Entity, typename Type>
 class SparseSet<Entity, Type>: public SparseSet<Entity> {
@@ -424,7 +424,7 @@ public:
 
     /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */
     SparseSet & operator=(const SparseSet &) = delete;
-    /*! @brief Default move operator. @return This sparse set. */
+    /*! @brief Default move assignment operator. @return This sparse set. */
     SparseSet & operator=(SparseSet &&) = default;
 
     /**
@@ -506,9 +506,9 @@ public:
      * An assertion will abort the execution at runtime in debug mode if the
      * sparse set already contains the given entity.
      *
-     * @tparam Args The type of the params used to construct the object.
+     * @tparam Args Types of arguments to use to construct the object.
      * @param entity A valid entity identifier.
-     * @param args The params to use to construct an object for the entity.
+     * @param args Parameters to use to construct an object for the entity.
      * @return The object associated to the entity.
      */
     template<typename... Args>

+ 141 - 10
src/entt/entity/view.hpp

@@ -3,6 +3,7 @@
 
 
 #include <tuple>
+#include <utility>
 #include "sparse_set.hpp"
 
 
@@ -46,7 +47,7 @@ namespace entt {
  * @sa View<Entity, Component>
  *
  * @tparam Entity A valid entity type (see entt_traits for more details).
- * @tparam Component The types of the components iterated by the view.
+ * @tparam Component Types of components iterated by the view.
  */
 template<typename Entity, typename... Component>
 class PersistentView final {
@@ -82,7 +83,7 @@ public:
 
     /**
      * @brief Returns the number of entities that have the given components.
-     * @return The number of entities that have the given components.
+     * @return Number of entities that have the given components.
      */
     size_type size() const noexcept {
         return view.size();
@@ -154,7 +155,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode if
      * the view doesn't contain the given entity.
      *
-     * @tparam Comp The type of the component to get.
+     * @tparam Comp Type of the component to get.
      * @param entity A valid entity identifier.
      * @return The component assigned to the entity.
      */
@@ -176,7 +177,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode if
      * the view doesn't contain the given entity.
      *
-     * @tparam Comp The type of the component to get.
+     * @tparam Comp Type of the component to get.
      * @param entity A valid entity identifier.
      * @return The component assigned to the entity.
      */
@@ -185,6 +186,50 @@ public:
         return const_cast<Comp &>(const_cast<const PersistentView *>(this)->get<Comp>(entity));
     }
 
+    /**
+     * @brief Iterate the entities and applies them the given function object.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to all the components of the
+     * view.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type, Component &...);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void each(Func &&func) {
+        for(auto entity: *this) {
+            std::forward<Func>(func)(entity, get<Component>(entity)...);
+        }
+    }
+
+    /**
+     * @brief Iterate the entities and applies them the given function object.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of const references to all the components of the
+     * view.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type, const Component &...);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void each(Func &&func) const {
+        for(auto entity: *this) {
+            std::forward<Func>(func)(entity, get<Component>(entity)...);
+        }
+    }
+
     /**
      * @brief Sort the shared pool of entities according to the given component.
      *
@@ -199,7 +244,7 @@ public:
      * the pools of components can quickly ruin the order imposed to the pool of
      * entities shared between the persistent views.
      *
-     * @tparam Comp The type of the component to use to impose the order.
+     * @tparam Comp Type of the component to use to impose the order.
      */
     template<typename Comp>
     void sort() {
@@ -379,7 +424,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode if
      * the view doesn't contain the given entity.
      *
-     * @tparam Component The type of the component to get.
+     * @tparam Component Type of the component to get.
      * @param entity A valid entity identifier.
      * @return The component assigned to the entity.
      */
@@ -401,7 +446,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode if
      * the view doesn't contain the given entity.
      *
-     * @tparam Component The type of the component to get.
+     * @tparam Component Type of the component to get.
      * @param entity A valid entity identifier.
      * @return The component assigned to the entity.
      */
@@ -410,6 +455,50 @@ public:
         return const_cast<Component &>(const_cast<const View *>(this)->get<Component>(entity));
     }
 
+    /**
+     * @brief Iterate the entities and applies them the given function object.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to all the components of the
+     * view.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type, Component &...);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void each(Func &&func) {
+        for(auto entity: *this) {
+            std::forward<Func>(func)(entity, get<First>(entity), get<Other>(entity)...);
+        }
+    }
+
+    /**
+     * @brief Iterate the entities and applies them the given function object.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of const references to all the components of the
+     * view.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type, const Component &...);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void each(Func &&func) const {
+        for(auto entity: *this) {
+            std::forward<Func>(func)(entity, get<First>(entity), get<Other>(entity)...);
+        }
+    }
+
     /**
      * @brief Resets the view and reinitializes it.
      *
@@ -467,7 +556,7 @@ private:
  * @sa PersistentView
  *
  * @tparam Entity A valid entity type (see entt_traits for more details).
- * @tparam Component The type of the component iterated by the view.
+ * @tparam Component Type of the component iterated by the view.
  */
 template<typename Entity, typename Component>
 class View<Entity, Component> final {
@@ -480,7 +569,7 @@ public:
     using entity_type = typename pool_type::entity_type;
     /*! @brief Unsigned integer type. */
     using size_type = typename pool_type::size_type;
-    /*! The type of the component iterated by the view. */
+    /*! Type of the component iterated by the view. */
     using raw_type = typename pool_type::type;
 
     /**
@@ -493,7 +582,7 @@ public:
 
     /**
      * @brief Returns the number of entities that have the given component.
-     * @return The number of entities that have the given component.
+     * @return Number of entities that have the given component.
      */
     size_type size() const noexcept {
         return pool.size();
@@ -622,6 +711,48 @@ public:
         return const_cast<Component &>(const_cast<const View *>(this)->get(entity));
     }
 
+    /**
+     * @brief Iterate the entities and applies them the given function object.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a reference to the component of the view.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type, Component &);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void each(Func &&func) {
+        for(auto entity: *this) {
+            std::forward<Func>(func)(entity, get(entity));
+        }
+    }
+
+    /**
+     * @brief Iterate the entities and applies them the given function object.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a const reference to the component of the view.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(entity_type, const Component &);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void each(Func &&func) const {
+        for(auto entity: *this) {
+            std::forward<Func>(func)(entity, get(entity));
+        }
+    }
+
 private:
     pool_type &pool;
 };

+ 39 - 283
test/entt/entity/benchmark.cpp

@@ -37,7 +37,7 @@ TEST(DefaultRegistry, Construct) {
 
     Timer timer;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create();
     }
 
@@ -50,13 +50,13 @@ TEST(DefaultRegistry, Destroy) {
 
     std::cout << "Destroying 10000000 entities" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         entities.push_back(registry.create());
     }
 
     Timer timer;
 
-    for (auto entity: entities) {
+    for(auto entity: entities) {
         registry.destroy(entity);
     }
 
@@ -92,19 +92,12 @@ TEST(DefaultRegistry, IterateSingleComponent10M) {
 
     std::cout << "Iterating over 10000000 entities, one component" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create<Position>();
     }
 
     Timer timer;
-
-    auto view = registry.view<Position>();
-
-    for(auto entity: view) {
-        auto &position = view.get(entity);
-        (void)position;
-    }
-
+    registry.view<Position>().each([](auto, auto &) {});
     timer.elapsed();
 }
 
@@ -113,21 +106,12 @@ TEST(DefaultRegistry, IterateTwoComponents10M) {
 
     std::cout << "Iterating over 10000000 entities, two components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create<Position, Velocity>();
     }
 
     Timer timer;
-
-    auto view = registry.view<Position, Velocity>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        (void)position;
-        (void)velocity;
-    }
-
+    registry.view<Position, Velocity>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -136,22 +120,13 @@ TEST(DefaultRegistry, IterateTwoComponents10MHalf) {
 
     std::cout << "Iterating over 10000000 entities, two components, half of the entities have all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity>();
         if(i % 2) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.view<Position, Velocity>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        (void)position;
-        (void)velocity;
-    }
-
+    registry.view<Position, Velocity>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -160,22 +135,13 @@ TEST(DefaultRegistry, IterateTwoComponents10MOne) {
 
     std::cout << "Iterating over 10000000 entities, two components, only one entity has all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity>();
         if(i == 5000000L) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.view<Position, Velocity>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        (void)position;
-        (void)velocity;
-    }
-
+    registry.view<Position, Velocity>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -185,21 +151,12 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10M) {
 
     std::cout << "Iterating over 10000000 entities, two components, persistent view" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create<Position, Velocity>();
     }
 
     Timer timer;
-
-    auto view = registry.persistent<Position, Velocity>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        (void)position;
-        (void)velocity;
-    }
-
+    registry.persistent<Position, Velocity>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -209,22 +166,13 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10MHalf) {
 
     std::cout << "Iterating over 10000000 entities, two components, persistent view, half of the entities have all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity>();
         if(i % 2) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.persistent<Position, Velocity>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        (void)position;
-        (void)velocity;
-    }
-
+    registry.persistent<Position, Velocity>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -234,22 +182,13 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10MOne) {
 
     std::cout << "Iterating over 10000000 entities, two components, persistent view, only one entity has all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity>();
         if(i == 5000000L) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.persistent<Position, Velocity>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        (void)position;
-        (void)velocity;
-    }
-
+    registry.persistent<Position, Velocity>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -258,27 +197,12 @@ TEST(DefaultRegistry, IterateFiveComponents10M) {
 
     std::cout << "Iterating over 10000000 entities, five components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
     }
 
     Timer timer;
-
-    auto view = registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-    }
-
+    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -287,37 +211,12 @@ TEST(DefaultRegistry, IterateTenComponents10M) {
 
     std::cout << "Iterating over 10000000 entities, ten components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
     }
 
     Timer timer;
-
-    auto view = registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        auto &comp4 = view.get<Comp<4>>(entity);
-        auto &comp5 = view.get<Comp<5>>(entity);
-        auto &comp6 = view.get<Comp<6>>(entity);
-        auto &comp7 = view.get<Comp<7>>(entity);
-        auto &comp8 = view.get<Comp<8>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-        (void)comp4;
-        (void)comp5;
-        (void)comp6;
-        (void)comp7;
-        (void)comp8;
-    }
-
+    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -326,38 +225,13 @@ TEST(DefaultRegistry, IterateTenComponents10MHalf) {
 
     std::cout << "Iterating over 10000000 entities, ten components, half of the entities have all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
         if(i % 2) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        auto &comp4 = view.get<Comp<4>>(entity);
-        auto &comp5 = view.get<Comp<5>>(entity);
-        auto &comp6 = view.get<Comp<6>>(entity);
-        auto &comp7 = view.get<Comp<7>>(entity);
-        auto &comp8 = view.get<Comp<8>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-        (void)comp4;
-        (void)comp5;
-        (void)comp6;
-        (void)comp7;
-        (void)comp8;
-    }
-
+    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -366,38 +240,13 @@ TEST(DefaultRegistry, IterateTenComponents10MOne) {
 
     std::cout << "Iterating over 10000000 entities, ten components, only one entity has all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
         if(i == 5000000L) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        auto &comp4 = view.get<Comp<4>>(entity);
-        auto &comp5 = view.get<Comp<5>>(entity);
-        auto &comp6 = view.get<Comp<6>>(entity);
-        auto &comp7 = view.get<Comp<7>>(entity);
-        auto &comp8 = view.get<Comp<8>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-        (void)comp4;
-        (void)comp5;
-        (void)comp6;
-        (void)comp7;
-        (void)comp8;
-    }
-
+    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -407,27 +256,12 @@ TEST(DefaultRegistry, IterateFiveComponentsPersistent10M) {
 
     std::cout << "Iterating over 10000000 entities, five components, persistent view" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
     }
 
     Timer timer;
-
-    auto view = registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-    }
-
+    registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -437,37 +271,12 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10M) {
 
     std::cout << "Iterating over 10000000 entities, ten components, persistent view" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
     }
 
     Timer timer;
-
-    auto view = registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        auto &comp4 = view.get<Comp<4>>(entity);
-        auto &comp5 = view.get<Comp<5>>(entity);
-        auto &comp6 = view.get<Comp<6>>(entity);
-        auto &comp7 = view.get<Comp<7>>(entity);
-        auto &comp8 = view.get<Comp<8>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-        (void)comp4;
-        (void)comp5;
-        (void)comp6;
-        (void)comp7;
-        (void)comp8;
-    }
-
+    registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -477,38 +286,13 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10MHalf) {
 
     std::cout << "Iterating over 10000000 entities, ten components, persistent view, half of the entities have all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
         if(i % 2) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        auto &comp4 = view.get<Comp<4>>(entity);
-        auto &comp5 = view.get<Comp<5>>(entity);
-        auto &comp6 = view.get<Comp<6>>(entity);
-        auto &comp7 = view.get<Comp<7>>(entity);
-        auto &comp8 = view.get<Comp<8>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-        (void)comp4;
-        (void)comp5;
-        (void)comp6;
-        (void)comp7;
-        (void)comp8;
-    }
-
+    registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -518,38 +302,13 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10MOne) {
 
     std::cout << "Iterating over 10000000 entities, ten components, persistent view, only one entity has all the components" << std::endl;
 
-    for (uint64_t i = 0; i < 10000000L; i++) {
+    for(uint64_t i = 0; i < 10000000L; i++) {
         auto entity = registry.create<Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
         if(i == 5000000L) { registry.assign<Position>(entity); }
     }
 
     Timer timer;
-
-    auto view = registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
-
-    for(auto entity: view) {
-        auto &position = view.get<Position>(entity);
-        auto &velocity = view.get<Velocity>(entity);
-        auto &comp1 = view.get<Comp<1>>(entity);
-        auto &comp2 = view.get<Comp<2>>(entity);
-        auto &comp3 = view.get<Comp<3>>(entity);
-        auto &comp4 = view.get<Comp<4>>(entity);
-        auto &comp5 = view.get<Comp<5>>(entity);
-        auto &comp6 = view.get<Comp<6>>(entity);
-        auto &comp7 = view.get<Comp<7>>(entity);
-        auto &comp8 = view.get<Comp<8>>(entity);
-        (void)position;
-        (void)velocity;
-        (void)comp1;
-        (void)comp2;
-        (void)comp3;
-        (void)comp4;
-        (void)comp5;
-        (void)comp6;
-        (void)comp7;
-        (void)comp8;
-    }
-
+    registry.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
     timer.elapsed();
 }
 
@@ -559,15 +318,14 @@ TEST(DefaultRegistry, SortSingle) {
 
     std::cout << "Sort 150000 entities, one component" << std::endl;
 
-    for (uint64_t i = 0; i < 150000L; i++) {
-        auto entity = registry.create();
+    for(uint64_t i = 0; i < 150000L; i++) {
+        auto entity = registry.create<Position>({ i, i });
         entities.push_back(entity);
-        registry.assign<Position>(entity, i, i);
     }
 
     Timer timer;
 
-    registry.sort<Position>([&registry](const auto &lhs, const auto &rhs) {
+    registry.sort<Position>([](const auto &lhs, const auto &rhs) {
         return lhs.x < rhs.x && lhs.y < rhs.y;
     });
 
@@ -580,14 +338,12 @@ TEST(DefaultRegistry, SortMulti) {
 
     std::cout << "Sort 150000 entities, two components" << std::endl;
 
-    for (uint64_t i = 0; i < 150000L; i++) {
-        auto entity = registry.create();
+    for(uint64_t i = 0; i < 150000L; i++) {
+        auto entity = registry.create<Position, Velocity>({ i, i }, { i, i });
         entities.push_back(entity);
-        registry.assign<Position>(entity, i, i);
-        registry.assign<Velocity>(entity, i, i);
     }
 
-    registry.sort<Position>([&registry](const auto &lhs, const auto &rhs) {
+    registry.sort<Position>([](const auto &lhs, const auto &rhs) {
         return lhs.x < rhs.x && lhs.y < rhs.y;
     });
 

+ 9 - 21
test/entt/entity/registry.cpp

@@ -137,15 +137,11 @@ TEST(DefaultRegistry, CreateDestroyEntities) {
 TEST(DefaultRegistry, SortSingle) {
     entt::DefaultRegistry registry;
 
-    auto e1 = registry.create();
-    auto e2 = registry.create();
-    auto e3 = registry.create();
+    int val = 0;
 
-    auto val = 0;
-
-    registry.assign<int>(e1, val++);
-    registry.assign<int>(e2, val++);
-    registry.assign<int>(e3, val++);
+    registry.create(val++);
+    registry.create(val++);
+    registry.create(val++);
 
     for(auto entity: registry.view<int>()) {
         ASSERT_EQ(registry.get<int>(entity), --val);
@@ -161,20 +157,12 @@ TEST(DefaultRegistry, SortSingle) {
 TEST(DefaultRegistry, SortMulti) {
     entt::DefaultRegistry registry;
 
-    auto e1 = registry.create();
-    auto e2 = registry.create();
-    auto e3 = registry.create();
-
-    auto uval = 0u;
-    auto ival = 0;
-
-    registry.assign<unsigned int>(e1, uval++);
-    registry.assign<unsigned int>(e2, uval++);
-    registry.assign<unsigned int>(e3, uval++);
+    unsigned int uval = 0u;
+    int ival = 0;
 
-    registry.assign<int>(e1, ival++);
-    registry.assign<int>(e2, ival++);
-    registry.assign<int>(e3, ival++);
+    registry.create(uval++, ival++);
+    registry.create(uval++, ival++);
+    registry.create(uval++, ival++);
 
     for(auto entity: registry.view<unsigned int>()) {
         ASSERT_EQ(registry.get<unsigned int>(entity), --uval);

+ 61 - 3
test/entt/entity/view.cpp

@@ -56,6 +56,25 @@ TEST(View, SingleComponentEmpty) {
     }
 }
 
+TEST(View, SingleComponentEach) {
+    entt::DefaultRegistry registry;
+
+    registry.create<int, char>();
+    registry.create<int, char>();
+
+    auto view = registry.view<int>();
+    const auto &cview = static_cast<const decltype(view) &>(view);
+    std::size_t cnt = 0;
+
+    view.each([&cnt](auto, int &) { ++cnt; });
+
+    ASSERT_EQ(cnt, std::size_t{2});
+
+    cview.each([&cnt](auto, const int &) { --cnt; });
+
+    ASSERT_EQ(cnt, std::size_t{0});
+}
+
 TEST(View, MultipleComponent) {
     entt::DefaultRegistry registry;
 
@@ -98,7 +117,26 @@ TEST(View, MultipleComponentEmpty) {
     }
 }
 
-TEST(PersistentView, MultipleComponentPrepare) {
+TEST(View, MultipleComponentEach) {
+    entt::DefaultRegistry registry;
+
+    registry.create<int, char>();
+    registry.create<int, char>();
+
+    auto view = registry.view<int, char>();
+    const auto &cview = static_cast<const decltype(view) &>(view);
+    std::size_t cnt = 0;
+
+    view.each([&cnt](auto, int &, char &) { ++cnt; });
+
+    ASSERT_EQ(cnt, std::size_t{2});
+
+    cview.each([&cnt](auto, const int &, const char &) { --cnt; });
+
+    ASSERT_EQ(cnt, std::size_t{0});
+}
+
+TEST(PersistentView, Prepare) {
     entt::DefaultRegistry registry;
     registry.prepare<int, char>();
 
@@ -137,7 +175,7 @@ TEST(PersistentView, MultipleComponentPrepare) {
     ASSERT_EQ(view.begin(), view.end());
 }
 
-TEST(PersistentView, MultipleComponentNoPrepare) {
+TEST(PersistentView, NoPrepare) {
     entt::DefaultRegistry registry;
 
     auto e1 = registry.create<char>();
@@ -175,7 +213,7 @@ TEST(PersistentView, MultipleComponentNoPrepare) {
     ASSERT_EQ(view.begin(), view.end());
 }
 
-TEST(PersistentView, MultipleComponentEmpty) {
+TEST(PersistentView, Empty) {
     entt::DefaultRegistry registry;
 
     registry.create<double, int, float>();
@@ -192,6 +230,26 @@ TEST(PersistentView, MultipleComponentEmpty) {
     }
 }
 
+TEST(PersistentView, Each) {
+    entt::DefaultRegistry registry;
+    registry.prepare<int, char>();
+
+    registry.create<int, char>();
+    registry.create<int, char>();
+
+    auto view = registry.persistent<int, char>();
+    const auto &cview = static_cast<const decltype(view) &>(view);
+    std::size_t cnt = 0;
+
+    view.each([&cnt](auto, int &, char &) { ++cnt; });
+
+    ASSERT_EQ(cnt, std::size_t{2});
+
+    cview.each([&cnt](auto, const int &, const char &) { --cnt; });
+
+    ASSERT_EQ(cnt, std::size_t{0});
+}
+
 TEST(PersistentView, Sort) {
     entt::DefaultRegistry registry;
     registry.prepare<int, unsigned int>();