observer (they call me reactive system)
This commit is contained in:
@@ -24,6 +24,10 @@ class basic_runtime_view;
|
||||
template<typename...>
|
||||
class basic_group;
|
||||
|
||||
/*! @class basic_observer */
|
||||
template<typename>
|
||||
class basic_observer;
|
||||
|
||||
/*! @class basic_actor */
|
||||
template <typename>
|
||||
struct basic_actor;
|
||||
@@ -50,6 +54,9 @@ using entity = std::uint32_t;
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using actor = basic_actor<entity>;
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ class basic_group;
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
@@ -433,6 +434,7 @@ private:
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
|
||||
357
src/entt/entity/observer.hpp
Normal file
357
src/entt/entity/observer.hpp
Normal file
@@ -0,0 +1,357 @@
|
||||
#ifndef ENTT_ENTITY_OBSERVER_HPP
|
||||
#define ENTT_ENTITY_OBSERVER_HPP
|
||||
|
||||
|
||||
#include <limits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Matcher.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename...>
|
||||
struct matcher;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Observing matcher.
|
||||
*
|
||||
* An observing matcher contains a type for which changes should be
|
||||
* detected.<br/>
|
||||
* Because of the rules of the language, not all changes can be easily detected.
|
||||
* In order to avoid nasty solutions that could affect performance to an extent,
|
||||
* the matcher listens only to the `on_replace` signals emitted by a registry
|
||||
* and is therefore triggered whenever an instance of the given component is
|
||||
* explicitly replaced.
|
||||
*
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
struct matcher<AnyOf> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Grouping matcher.
|
||||
*
|
||||
* A grouping matcher describes the group to track in terms of accepted and
|
||||
* excluded types.<br/>
|
||||
* This kind of matcher is triggered whenever an entity _enters_ the desired
|
||||
* group because of the components it is assigned.
|
||||
*
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
struct matcher<type_list<AllOf...>, type_list<NoneOf...>> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
*
|
||||
* A collector contains a set of rules (literally, matchers) to use to track
|
||||
* entities.<br/>
|
||||
* Its main purpose is to generate a descriptor that allows an observer to know
|
||||
* how to connect to a registry.
|
||||
*
|
||||
* @tparam AnyOf Types of components for which changes should be detected.
|
||||
* @tparam Matcher Types of grouping matchers.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
struct basic_collector {
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<Matcher..., matcher<type_list<AllOf...>, type_list<NoneOf...>>>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds one or more observing matchers to the collector.
|
||||
* @tparam AnyOf Types of components for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AnyOf>
|
||||
static constexpr auto replace() ENTT_NOEXCEPT {
|
||||
return basic_collector<Matcher..., matcher<AnyOf>...>{};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Variable template used to ease the definition of collectors. */
|
||||
constexpr basic_collector<> collector{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Observer.
|
||||
*
|
||||
* An observer returns all the entities and only the entities that fit the
|
||||
* requirements of at least one matcher. Moreover, it's guaranteed that the
|
||||
* entity list is tightly packed in memory for fast iterations.<br/>
|
||||
* In general, observers don't stay true to the order of any set of components.
|
||||
*
|
||||
* Observers work mainly with two types of matchers, provided through a
|
||||
* collector:
|
||||
*
|
||||
* * Observing matcher: an observer will return at least all the living entities
|
||||
* for which one or more of the given components have been explicitly
|
||||
* replaced and not yet destroyed.
|
||||
* * Grouping matcher: an observer will return at least all the living entities
|
||||
* that would have entered the given group if it existed and that would have
|
||||
* not yet left it.
|
||||
*
|
||||
* If an entity respects the requirements of multiple matchers, it will be
|
||||
* returned once and only once by the observer in any case.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of an observer doesn't necessarily have to overcome the one of the
|
||||
* registry to which it is connected. However, the observer must be disconnected
|
||||
* from the registry before being destroyed to avoid crashes due to dangling
|
||||
* pointers.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_observer {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
using payload_type = std::uint32_t;
|
||||
|
||||
template<std::size_t Index, typename>
|
||||
struct matcher_handler;
|
||||
|
||||
template<std::size_t Index, typename AnyOf>
|
||||
struct matcher_handler<Index, matcher<AnyOf>> {
|
||||
static void maybe_valid_if(basic_observer *obs, const basic_registry<Entity> &, const Entity entt) {
|
||||
(obs->view.has(entt) ? obs->view.get(entt) : obs->view.construct(entt)) |= (1 << Index);
|
||||
}
|
||||
|
||||
static void discard_if(basic_observer *obs, const basic_registry<Entity> &, const Entity entt) {
|
||||
if(auto *value = obs->view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
|
||||
obs->view.destroy(entt);
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnect(basic_registry<Entity> ®, const basic_observer &obs) {
|
||||
(reg.template on_replace<AnyOf>().disconnect(&obs));
|
||||
(reg.template on_destroy<AnyOf>().disconnect(&obs));
|
||||
}
|
||||
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
reg.template on_replace<AnyOf>().template connect<&maybe_valid_if>(&obs);
|
||||
reg.template on_destroy<AnyOf>().template connect<&discard_if>(&obs);
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t Index, typename... AllOf, typename... NoneOf>
|
||||
struct matcher_handler<Index, matcher<type_list<AllOf...>, type_list<NoneOf...>>> {
|
||||
static void maybe_valid_if(basic_observer *obs, const basic_registry<Entity> ®, const Entity entt) {
|
||||
if(reg.template has<AllOf...>(entt) && !(reg.template has<NoneOf>(entt) || ...)) {
|
||||
(obs->view.has(entt) ? obs->view.get(entt) : obs->view.construct(entt)) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
static void discard_if(basic_observer *obs, const basic_registry<Entity> &, const Entity entt) {
|
||||
if(auto *value = obs->view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
|
||||
obs->view.destroy(entt);
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnect(basic_registry<Entity> ®, const basic_observer &obs) {
|
||||
((reg.template on_construct<AllOf>().disconnect(&obs)), ...);
|
||||
((reg.template on_destroy<AllOf>().disconnect(&obs)), ...);
|
||||
((reg.template on_construct<NoneOf>().disconnect(&obs)), ...);
|
||||
((reg.template on_destroy<NoneOf>().disconnect(&obs)), ...);
|
||||
}
|
||||
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if>(&obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if>(&obs), ...);
|
||||
(reg.template on_destroy<AllOf>().template connect<&discard_if>(&obs), ...);
|
||||
(reg.template on_construct<NoneOf>().template connect<&discard_if>(&obs), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template<auto... Disconnect>
|
||||
static void disconnect(basic_registry<Entity> ®, const basic_observer &obs) {
|
||||
(Disconnect(reg, obs), ...);
|
||||
}
|
||||
|
||||
template<typename... Matcher, std::size_t... Index>
|
||||
void connect(basic_registry<Entity> ®, std::index_sequence<Index...>) {
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits);
|
||||
release = &basic_observer::disconnect<&matcher_handler<Index, Matcher>::disconnect...>;
|
||||
(matcher_handler<Index, Matcher>::connect(*this, reg), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename traits_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename sparse_set<Entity>::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename sparse_set<Entity>::iterator_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_observer() ENTT_NOEXCEPT
|
||||
: target{}, release{}, view{}
|
||||
{}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_observer(const basic_observer &) = delete;
|
||||
/*! @brief Default move constructor, deleted on purpose. */
|
||||
basic_observer(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
*/
|
||||
basic_observer & operator=(const basic_observer &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
*/
|
||||
basic_observer & operator=(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Creates an observer and connects it to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
basic_observer(basic_registry<entity_type> ®, basic_collector<Matcher...>) ENTT_NOEXCEPT
|
||||
: target{®},
|
||||
release{},
|
||||
view{}
|
||||
{
|
||||
connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_observer() = default;
|
||||
|
||||
/**
|
||||
* @brief Connects an observer to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) {
|
||||
release ? release(*target, *this) : void();
|
||||
connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
|
||||
target = ®
|
||||
view.reset();
|
||||
}
|
||||
|
||||
/*! @brief Disconnects an observer from the registry it keeps track of. */
|
||||
void disconnect() {
|
||||
if(release) {
|
||||
release(*target, *this);
|
||||
release = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in an observer.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
return view.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether an observer is empty.
|
||||
* @return True if the observer is empty, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
return view.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities of the observer.
|
||||
*
|
||||
* The returned pointer is such that range `[data(), data() + size()]` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* There are no guarantees on the order of the entities. Use `begin` and
|
||||
* `end` if you want to iterate the observer in the expected order.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return view.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the first entity of the observer. If the
|
||||
* container is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the observer.
|
||||
*/
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return view.sparse_set<entity_type>::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the observer. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* observer.
|
||||
*/
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
return view.sparse_set<entity_type>::end();
|
||||
}
|
||||
|
||||
/*! @brief Resets the underlying container. */
|
||||
void clear() {
|
||||
return view.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry<entity_type> *target;
|
||||
void(* release)(basic_registry<Entity> &, const basic_observer &);
|
||||
storage<entity_type, payload_type> view;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_OBSERVER_HPP
|
||||
@@ -35,6 +35,7 @@ namespace entt {
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace entt {
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
@@ -518,6 +519,7 @@ private:
|
||||
* * New instances of the given component are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, the given
|
||||
* component is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pool of the given component in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "entity/entity.hpp"
|
||||
#include "entity/group.hpp"
|
||||
#include "entity/helper.hpp"
|
||||
#include "entity/observer.hpp"
|
||||
#include "entity/prototype.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
#include "entity/runtime_view.hpp"
|
||||
|
||||
@@ -224,9 +224,7 @@ public:
|
||||
}), calls->end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects all the listeners from a signal.
|
||||
*/
|
||||
/*! @brief Disconnects all the listeners from a signal. */
|
||||
void disconnect() {
|
||||
calls->clear();
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ SETUP_AND_ADD_TEST(actor entt/entity/actor.cpp)
|
||||
SETUP_AND_ADD_TEST(entity entt/entity/entity.cpp)
|
||||
SETUP_AND_ADD_TEST(group entt/entity/group.cpp)
|
||||
SETUP_AND_ADD_TEST(helper entt/entity/helper.cpp)
|
||||
SETUP_AND_ADD_TEST(observer entt/entity/observer.cpp)
|
||||
SETUP_AND_ADD_TEST(prototype entt/entity/prototype.cpp)
|
||||
SETUP_AND_ADD_TEST(registry entt/entity/registry.cpp)
|
||||
SETUP_AND_ADD_TEST(runtime_view entt/entity/runtime_view.cpp)
|
||||
|
||||
169
test/entt/entity/observer.cpp
Normal file
169
test/entt/entity/observer.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/observer.hpp>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
TEST(Observer, Functionalities) {
|
||||
entt::registry registry;
|
||||
entt::observer observer{registry, entt::collector.group<int>()};
|
||||
|
||||
ASSERT_EQ(observer.size(), entt::observer::size_type{});
|
||||
ASSERT_TRUE(observer.empty());
|
||||
ASSERT_EQ(observer.data(), nullptr);
|
||||
ASSERT_EQ(observer.begin(), observer.end());
|
||||
|
||||
const auto entity = std::get<0>(registry.create<int>());
|
||||
|
||||
ASSERT_EQ(observer.size(), entt::observer::size_type{1});
|
||||
ASSERT_FALSE(observer.empty());
|
||||
ASSERT_NE(observer.data(), nullptr);
|
||||
ASSERT_EQ(*observer.data(), entity);
|
||||
ASSERT_NE(observer.begin(), observer.end());
|
||||
ASSERT_EQ(++observer.begin(), observer.end());
|
||||
ASSERT_EQ(*observer.begin(), entity);
|
||||
|
||||
observer.clear();
|
||||
|
||||
ASSERT_EQ(observer.size(), entt::observer::size_type{});
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
observer.disconnect();
|
||||
registry.remove<int>(entity);
|
||||
registry.assign<int>(entity);
|
||||
|
||||
ASSERT_EQ(observer.size(), entt::observer::size_type{});
|
||||
ASSERT_TRUE(observer.empty());
|
||||
}
|
||||
|
||||
TEST(Observer, AllOf) {
|
||||
constexpr auto collector = entt::collector
|
||||
.group<int, char>(entt::exclude<float>)
|
||||
.group<int, double>();
|
||||
|
||||
entt::registry registry;
|
||||
entt::observer observer{registry, collector};
|
||||
const auto entity = registry.create();
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
ASSERT_EQ(observer.size(), entt::observer::size_type{1});
|
||||
ASSERT_FALSE(observer.empty());
|
||||
ASSERT_EQ(*observer.data(), entity);
|
||||
|
||||
registry.assign<double>(entity);
|
||||
|
||||
ASSERT_FALSE(observer.empty());
|
||||
|
||||
registry.remove<int>(entity);
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.assign<float>(entity);
|
||||
registry.assign<int>(entity);
|
||||
|
||||
ASSERT_FALSE(observer.empty());
|
||||
|
||||
registry.remove<double>(entity);
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.assign<double>(entity);
|
||||
observer.clear();
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
observer.disconnect();
|
||||
registry.assign_or_replace<int>(entity);
|
||||
registry.assign_or_replace<char>(entity);
|
||||
registry.reset<float>(entity);
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
}
|
||||
|
||||
TEST(Observer, Observe) {
|
||||
entt::registry registry;
|
||||
entt::observer observer{registry, entt::collector.replace<int, char>()};
|
||||
const auto entity = registry.create();
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.assign_or_replace<int>(entity);
|
||||
|
||||
ASSERT_EQ(observer.size(), entt::observer::size_type{1});
|
||||
ASSERT_FALSE(observer.empty());
|
||||
ASSERT_EQ(*observer.data(), entity);
|
||||
|
||||
observer.clear();
|
||||
registry.replace<char>(entity);
|
||||
|
||||
ASSERT_FALSE(observer.empty());
|
||||
|
||||
observer.clear();
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
observer.disconnect();
|
||||
registry.assign_or_replace<int>(entity);
|
||||
registry.assign_or_replace<char>(entity);
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
}
|
||||
|
||||
TEST(Observer, AllOfObserve) {
|
||||
entt::registry registry;
|
||||
entt::observer observer{};
|
||||
const auto entity = registry.create();
|
||||
|
||||
observer.connect(registry, entt::collector.group<int>().replace<char>());
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
registry.replace<char>(entity);
|
||||
registry.remove<int>(entity);
|
||||
|
||||
ASSERT_EQ(observer.size(), entt::observer::size_type{1});
|
||||
ASSERT_FALSE(observer.empty());
|
||||
ASSERT_EQ(*observer.data(), entity);
|
||||
|
||||
registry.remove<char>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.replace<char>(entity);
|
||||
observer.clear();
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
observer.disconnect();
|
||||
registry.assign_or_replace<int>(entity);
|
||||
registry.assign_or_replace<char>(entity);
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
}
|
||||
|
||||
|
||||
TEST(Observer, CrossRulesCornerCase) {
|
||||
entt::registry registry;
|
||||
entt::observer observer{registry, entt::collector.group<int>().group<char>()};
|
||||
const auto entity = registry.create();
|
||||
|
||||
registry.assign<int>(entity);
|
||||
observer.clear();
|
||||
|
||||
ASSERT_TRUE(observer.empty());
|
||||
|
||||
registry.assign<char>(entity);
|
||||
registry.remove<int>(entity);
|
||||
|
||||
ASSERT_FALSE(observer.empty());
|
||||
}
|
||||
Reference in New Issue
Block a user