removed spaces: not satisfied with the current implementation
This commit is contained in:
114
README.md
114
README.md
@@ -25,7 +25,6 @@
|
||||
* [Multi component standard view](#multi-component-standard-view)
|
||||
* [Persistent View](#persistent-view)
|
||||
* [Give me everything](#give-me-everything)
|
||||
* [Spaces](#spaces)
|
||||
* [Side notes](#side-notes)
|
||||
* [Crash Course: core functionalities](#crash-course-core-functionalities)
|
||||
* [Compile-time identifiers](#compile-time-identifiers)
|
||||
@@ -947,119 +946,6 @@ In general, all these functions can result in poor performance.<br/>
|
||||
entity. For similar reasons, `orphans` can be even slower. Both functions should
|
||||
not be used frequently to avoid the risk of a performance hit.
|
||||
|
||||
## Spaces
|
||||
|
||||
Spaces are sort of partitions of a registry. They can be used to easily get a
|
||||
subset of the entities of a view or a registry without recurring to multiple
|
||||
registries to separate them explicitly.<br/>
|
||||
To learn more about their intended use,
|
||||
[here](https://gamedevelopment.tutsplus.com/tutorials/spaces-useful-game-object-containers--gamedev-14091)
|
||||
is an interesting article that goes deep into the topic.
|
||||
|
||||
Spaces aren't for free. In most of the cases, the cost isn't relevant. However,
|
||||
keep in mind that they add an extra check during iterations and it could slow
|
||||
down a bit the whole thing.<br/>
|
||||
Alternatives to spaces exist, but they have their own problems:
|
||||
|
||||
* Multiple registries: memory usage tends to grow up and some tasks are just
|
||||
more difficult to accomplish (as an example, putting an entity logically in
|
||||
more than one registry requires syncing them and it can quickly become a
|
||||
problem).
|
||||
|
||||
* Dedicated components: memory usage tends to grow up and the number of spaces
|
||||
is fixed and defined at compile-time (at least, it ought to be for performance
|
||||
reasons), moreover the solution is much more error-prone.
|
||||
|
||||
Another benefit of spaces defined as an external class is that users of a space
|
||||
do not have access to the whole registry, thus separation of responsibility is
|
||||
automatically enforced. In both the alternatives described above, systems have
|
||||
access to the whole set of entities instead and can easily break the contract
|
||||
with the callers.
|
||||
|
||||
The `EnTT` framework offers support to spaces out of the box. Spaces are
|
||||
constructed using a registry to which they refer:
|
||||
|
||||
```cpp
|
||||
entt::DefaultRegistry registry;
|
||||
entt::Space<typename entt::DefaultRegistry::entity_type> space{registry};
|
||||
```
|
||||
|
||||
They offer the classical set of member functions to know the estimated number of
|
||||
entities and to check if a space has a given entity.<br/>
|
||||
Refer to the [official documentation](https://skypjack.github.io/entt/) for all
|
||||
the details.
|
||||
|
||||
In addition, they expose two member functions to create an entity through a
|
||||
space or to assign to a space an already existent entity, other than member
|
||||
functions to remove entities from a space:
|
||||
|
||||
```cpp
|
||||
// creates an entity using a space
|
||||
auto entity = space.create();
|
||||
|
||||
// assigns an already existent entity to a space
|
||||
space.assign(registry.create());
|
||||
|
||||
// removes an entity from the given space
|
||||
space.remove(entity);
|
||||
|
||||
// removes all the entities from a space
|
||||
space.reset();
|
||||
```
|
||||
|
||||
Entities returned through the `create` member function are created directly into
|
||||
the underlying registry and assigned immediately to the space.<br/>
|
||||
Removing an entity from a space doesn't mean that it's destroyed within the
|
||||
underlying registry in any case.
|
||||
|
||||
Spaces and thus the entities they contain can be easily iterated in a range-for
|
||||
loop:
|
||||
|
||||
```cpp
|
||||
for(auto entity: space) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
However, this isn't the best way to iterate entities in a space, mainly because
|
||||
this member function returns all the entities it contains, no matter what are
|
||||
their components. To iterate entities that have specific components, spaces
|
||||
expose two dedicated member functions that differ for the view they use under
|
||||
the hood:
|
||||
|
||||
```cpp
|
||||
// uses a standard view internally
|
||||
space.view<AComponent, AnotherComponent>([](auto entity, auto &aComponent, auto &anotherComponent) {
|
||||
// ...
|
||||
});
|
||||
|
||||
// uses a persistent view internally
|
||||
space.persisten<AComponent, AnotherComponent>([](auto entity, auto &aComponent, auto &anotherComponent) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Spaces get rid of entities that are no longer in use during iterations. They
|
||||
aren't kept in sync with a registry each and every time an entity is destroyed
|
||||
so as to avoid penalties in terms of performance. Instead, spaces remove invalid
|
||||
entities as soon as they are detected during iterations.
|
||||
|
||||
Because of the _lazy clean_ policy, the size of a space could grow up if
|
||||
destroyed entities are never detected for some reasons. To avoid it, spaces has
|
||||
a member function named `shrink` that forces a clean up and reduce the size to a
|
||||
minimum:
|
||||
|
||||
```cpp
|
||||
// gets rid of all the invalid entities still tracked by a space
|
||||
space.shrink();
|
||||
```
|
||||
|
||||
Note that the size of a space isn't a problem in terms of performance. Views
|
||||
rule during iterations, mainly because of the order which may have been imposed
|
||||
by the user for some reasons and must be respected. Therefore unused entities
|
||||
are never visited and thus they don't affect iterations. However, memory usage
|
||||
can be reduced by shrinking spaces every so often.
|
||||
|
||||
## Side notes
|
||||
|
||||
* Entity identifiers are numbers and nothing more. They are not classes and they
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
#ifndef ENTT_ENTITY_SPACE_HPP
|
||||
#define ENTT_ENTITY_SPACE_HPP
|
||||
|
||||
|
||||
#include <utility>
|
||||
#include "sparse_set.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief A space is a sort of partition of a registry.
|
||||
*
|
||||
* Spaces can be used to create partitions of a registry. They can be useful for
|
||||
* logically separating menus, world and any other type of scene, while still
|
||||
* using only one registry.<br/>
|
||||
* Similar results are obtained either using multiple registries or using
|
||||
* dedicated components, even though in both cases the memory usage isn't the
|
||||
* same. On the other side, spaces can introduce performance hits that are
|
||||
* sometimes unacceptable (mainly if you are working on AAA games or kind of).
|
||||
*
|
||||
* For more details about spaces and their use, take a look at this article:
|
||||
* https://gamedevelopment.tutsplus.com/tutorials/spaces-useful-game-object-containers--gamedev-14091
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class Space: private SparseSet<Entity> {
|
||||
using view_type = SparseSet<Entity>;
|
||||
|
||||
template<typename View, typename Func>
|
||||
inline void each(View view, Func func) {
|
||||
// use the view to iterate so as to respect order of components if any
|
||||
view.each([func = std::move(func), this](auto entity, auto &&... components) {
|
||||
if(this->has(entity)) {
|
||||
if(this->data()[this->get(entity)] == entity) {
|
||||
func(entity, std::forward<decltype(components)>(components)...);
|
||||
} else {
|
||||
// lazy destroy to avoid keeping a space in sync
|
||||
this->destroy(entity);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of registry to which the space refers. */
|
||||
using registry_type = Registry<Entity>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename view_type::iterator_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename view_type::size_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs a space by using the given registry.
|
||||
* @param registry An entity-component system properly initialized.
|
||||
*/
|
||||
Space(registry_type ®istry)
|
||||
: registry{registry}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities tracked by a space.
|
||||
* @return Number of entities tracked by the space.
|
||||
*/
|
||||
size_type size() const noexcept {
|
||||
return SparseSet<Entity>::size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if there exists at least an entity tracked by a space.
|
||||
* @return True if the space tracks at least an entity, false otherwise.
|
||||
*/
|
||||
bool empty() const noexcept {
|
||||
return SparseSet<Entity>::empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity tracked by a space.
|
||||
*
|
||||
* The returned iterator points to the first entity tracked by the space. If
|
||||
* the space is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity tracked by a space.
|
||||
*/
|
||||
iterator_type begin() const noexcept {
|
||||
return SparseSet<Entity>::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity tracked by a
|
||||
* space.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity
|
||||
* tracked by the space. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity tracked by a
|
||||
* space.
|
||||
*/
|
||||
iterator_type end() const noexcept {
|
||||
return SparseSet<Entity>::end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a space contains an entity.
|
||||
* @param entity A valid entity identifier.
|
||||
* @return True if the space contains the given entity, false otherwise.
|
||||
*/
|
||||
bool contains(entity_type entity) const noexcept {
|
||||
return this->has(entity) && this->data()[this->get(entity)] == entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new entity and returns it.
|
||||
*
|
||||
* The space creates an entity from the underlying registry and registers it
|
||||
* immediately before to return the identifier. Use the `assign` member
|
||||
* function to register an already existent entity created at a different
|
||||
* time.
|
||||
*
|
||||
* The returned entity has no components assigned.
|
||||
*
|
||||
* @return A valid entity identifier.
|
||||
*/
|
||||
entity_type create() {
|
||||
const auto entity = registry.create();
|
||||
assign(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a space.
|
||||
*
|
||||
* The space starts tracking the given entity and will return it during
|
||||
* iterations whenever required.<br/>
|
||||
* Entities can be assigned to more than one space at the same time.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid entity or to assign an entity that doesn't
|
||||
* belong to the underlying registry results in undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode in case of
|
||||
* invalid entity or if the registry doesn't own the entity.
|
||||
*
|
||||
* @param entity A valid entity identifier.
|
||||
*/
|
||||
void assign(entity_type entity) {
|
||||
assert(registry.valid(entity));
|
||||
|
||||
if(this->has(entity)) {
|
||||
this->destroy(entity);
|
||||
}
|
||||
|
||||
this->construct(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an entity from a space.
|
||||
*
|
||||
* The space stops tracking the given entity and won't return it anymore
|
||||
* during iterations.<br/>
|
||||
* In case the entity belongs to more than one space, it won't be removed
|
||||
* automatically from all the other ones as a consequence of invoking this
|
||||
* function.
|
||||
*
|
||||
* @param entity A valid entity identifier.
|
||||
*/
|
||||
void remove(entity_type entity) {
|
||||
if(this->has(entity)) {
|
||||
this->destroy(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities using a standard view under the hood.
|
||||
*
|
||||
* A space does not return directly views to iterate entities because it
|
||||
* requires to apply a filter to those sets. Instead, it uses a view
|
||||
* internally and returns only those entities that are tracked by the space
|
||||
* itself.<br/>
|
||||
* This member function can be used to iterate a space by means of a
|
||||
* standard view. Naming the function the same as the type of view used to
|
||||
* perform the task proved to be a good choice so as not to tricky users.
|
||||
*
|
||||
* @note
|
||||
* Performance tend to degenerate when the number of components to iterate
|
||||
* grows up and the most of the entities have all the given components.<br/>
|
||||
* To get a performance boost, consider using the `persistent` member
|
||||
* function instead.
|
||||
*
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename... Component, typename Func>
|
||||
void view(Func func) {
|
||||
each(registry.template view<Component...>(), std::move(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities using a persistent view under the hood.
|
||||
*
|
||||
* A space does not return directly views to iterate entities because it
|
||||
* requires to apply a filter to those sets. Instead, it uses a view
|
||||
* internally and returns only those entities that are tracked by the space
|
||||
* itself.<br/>
|
||||
* This member function can be used to iterate a space by means of a
|
||||
* persistent view. Naming the function the same as the type of view used to
|
||||
* perform the task proved to be a good choice so as not to tricky users.
|
||||
*
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename... Component, typename Func>
|
||||
void persistent(Func func) {
|
||||
each(registry.template persistent<Component...>(), std::move(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a clean up step.
|
||||
*
|
||||
* Spaces do a lazy cleanup during iterations to avoid introducing
|
||||
* performance hits when entities are destroyed.<br/>
|
||||
* This function can be used to force a clean up step and to get rid of all
|
||||
* those entities that are still tracked by a space but have been destroyed
|
||||
* in the underlying registry.
|
||||
*/
|
||||
void shrink() {
|
||||
for(auto entity: *this) {
|
||||
if(!registry.fast(entity)) {
|
||||
this->destroy(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets a whole space.
|
||||
*
|
||||
* The space stops tracking all the entities assigned to it so far. After
|
||||
* calling this function, iterations won't return any entity.
|
||||
*/
|
||||
void reset() {
|
||||
SparseSet<Entity>::reset();
|
||||
}
|
||||
|
||||
private:
|
||||
Registry<Entity> ®istry;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Default space class.
|
||||
*
|
||||
* The default space is the best choice for almost all the applications.<br/>
|
||||
* Users should have a really good reason to choose something different.
|
||||
*/
|
||||
using DefaultSpace = Space<DefaultRegistry::entity_type>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_SPACE_HPP
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "entity/actor.hpp"
|
||||
#include "entity/entt_traits.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
#include "entity/space.hpp"
|
||||
#include "entity/sparse_set.hpp"
|
||||
#include "entity/view.hpp"
|
||||
#include "locator/locator.hpp"
|
||||
|
||||
@@ -55,7 +55,6 @@ add_executable(
|
||||
$<TARGET_OBJECTS:odr>
|
||||
entt/entity/actor.cpp
|
||||
entt/entity/registry.cpp
|
||||
entt/entity/space.cpp
|
||||
entt/entity/sparse_set.cpp
|
||||
entt/entity/view.cpp
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <chrono>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/space.hpp>
|
||||
|
||||
struct Position {
|
||||
std::uint64_t x;
|
||||
@@ -315,152 +314,3 @@ TEST(Benchmark, SortMulti) {
|
||||
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceConstruct) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
std::cout << "Constructing 1000000 entities" << std::endl;
|
||||
|
||||
Timer timer;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
space.create();
|
||||
}
|
||||
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceAssign) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
std::cout << "Assigning 1000000 entities" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
registry.create<int>();
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
|
||||
for(auto entity: registry.view<int>()) {
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceIterateSingleComponent1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, one component" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create<Position>();
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
space.view<Position>([](auto, auto &) {});
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceIterateTwoComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create<Position, Velocity>();
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
space.view<Position, Velocity>([](auto, auto &...) {});
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceIterateTwoComponentsPersistent1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
registry.prepare<Position, Velocity>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, persistent view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create<Position, Velocity>();
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
space.persistent<Position, Velocity>([](auto, auto &...) {});
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceIterateFiveComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
space.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>([](auto, auto &...) {});
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceIterateFiveComponentsPersistent1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, persistent view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
space.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>([](auto, auto &...) {});
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceIterateTenComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
space.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>([](auto, auto &...) {});
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, SpaceIterateTenComponentsPersistent1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components, persistent view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
|
||||
space.assign(entity);
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
space.persistent<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>([](auto, auto &...) {});
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/space.hpp>
|
||||
|
||||
TEST(Space, SpaceContainsView) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
auto e0 = space.create();
|
||||
auto e1 = registry.create();
|
||||
|
||||
space.assign(e1);
|
||||
registry.assign<int>(e1);
|
||||
|
||||
ASSERT_TRUE(space.contains(e0));
|
||||
ASSERT_TRUE(space.contains(e1));
|
||||
|
||||
space.view<int>([e0, e1](auto entity, auto&&...) {
|
||||
ASSERT_NE(entity, e0);
|
||||
ASSERT_EQ(entity, e1);
|
||||
});
|
||||
|
||||
space.view<double>([e0, e1](auto, auto&&...) {
|
||||
FAIL();
|
||||
});
|
||||
|
||||
auto count = 0;
|
||||
|
||||
for(auto entity: space) {
|
||||
(void)entity;
|
||||
++count;
|
||||
}
|
||||
|
||||
ASSERT_EQ(count, 2);
|
||||
|
||||
const auto view = registry.view<int>();
|
||||
|
||||
ASSERT_EQ(view.size(), typename decltype(view)::size_type{1});
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{2});
|
||||
ASSERT_FALSE(space.empty());
|
||||
|
||||
registry.reset();
|
||||
space.reset();
|
||||
|
||||
ASSERT_TRUE(space.empty());
|
||||
|
||||
for(auto i = 0; i < 5; ++i) {
|
||||
registry.destroy(space.create());
|
||||
registry.create<int>();
|
||||
}
|
||||
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{5});
|
||||
ASSERT_FALSE(space.empty());
|
||||
|
||||
space.view<int>([](auto, auto&&...) {
|
||||
FAIL();
|
||||
});
|
||||
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{0});
|
||||
ASSERT_TRUE(space.empty());
|
||||
}
|
||||
|
||||
TEST(Space, ViewContainsSpace) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
auto e0 = registry.create();
|
||||
auto e1 = space.create();
|
||||
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<int>(e1);
|
||||
|
||||
ASSERT_FALSE(space.contains(e0));
|
||||
ASSERT_TRUE(space.contains(e1));
|
||||
|
||||
space.view<int>([e0, e1](auto entity, auto&&...) {
|
||||
ASSERT_NE(entity, e0);
|
||||
ASSERT_EQ(entity, e1);
|
||||
});
|
||||
|
||||
space.view<double>([e0, e1](auto, auto&&...) {
|
||||
FAIL();
|
||||
});
|
||||
|
||||
auto count = 0;
|
||||
|
||||
for(auto entity: space) {
|
||||
(void)entity;
|
||||
++count;
|
||||
}
|
||||
|
||||
ASSERT_EQ(count, 1);
|
||||
|
||||
const auto view = registry.view<int>();
|
||||
|
||||
ASSERT_EQ(view.size(), typename decltype(view)::size_type{2});
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{1});
|
||||
ASSERT_FALSE(space.empty());
|
||||
|
||||
registry.reset();
|
||||
space.reset();
|
||||
|
||||
ASSERT_TRUE(space.empty());
|
||||
|
||||
for(auto i = 0; i < 5; ++i) {
|
||||
registry.destroy(space.create());
|
||||
registry.create<int>();
|
||||
registry.create<int>();
|
||||
}
|
||||
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{5});
|
||||
ASSERT_FALSE(space.empty());
|
||||
|
||||
space.view<int>([](auto, auto&&...) {
|
||||
FAIL();
|
||||
});
|
||||
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{0});
|
||||
ASSERT_TRUE(space.empty());
|
||||
}
|
||||
|
||||
TEST(Space, AssignRemove) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
ASSERT_TRUE(space.empty());
|
||||
|
||||
space.remove(space.create());
|
||||
|
||||
ASSERT_TRUE(space.empty());
|
||||
}
|
||||
|
||||
TEST(Space, Shrink) {
|
||||
entt::DefaultRegistry registry;
|
||||
entt::DefaultSpace space{registry};
|
||||
|
||||
for(auto i = 0; i < 5; ++i) {
|
||||
space.create();
|
||||
}
|
||||
|
||||
for(auto entity: space) {
|
||||
registry.destroy(entity);
|
||||
}
|
||||
|
||||
space.create();
|
||||
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{5});
|
||||
ASSERT_FALSE(space.empty());
|
||||
|
||||
space.shrink();
|
||||
|
||||
ASSERT_EQ(space.size(), entt::DefaultSpace::size_type{1});
|
||||
ASSERT_FALSE(space.empty());
|
||||
}
|
||||
Reference in New Issue
Block a user