ソースを参照

doc: observer/reactive systems

Michele Caini 6 年 前
コミット
a5a4ee4bfe
4 ファイル変更111 行追加15 行削除
  1. 8 9
      README.md
  2. 0 1
      TODO
  3. 102 4
      docs/md/entity.md
  4. 1 1
      src/entt/entity/observer.hpp

+ 8 - 9
README.md

@@ -71,25 +71,24 @@ This project started off as a pure entity-component system. Over time the
 codebase has grown as more and more classes and functionalities were added.<br/>
 codebase has grown as more and more classes and functionalities were added.<br/>
 Here is a brief, yet incomplete list of what it offers today:
 Here is a brief, yet incomplete list of what it offers today:
 
 
-* Statically generated integer **identifiers for types** (assigned either at
+* Statically generated integer **identifiers** for types (assigned either at
   compile-time or at runtime).
   compile-time or at runtime).
-* A `constexpr` utility for **human readable resource identifiers**.
+* A `constexpr` utility for human readable **resource names**.
 * A minimal **configuration system** built using the monostate pattern.
 * A minimal **configuration system** built using the monostate pattern.
 * An incredibly fast **entity-component system** based on sparse sets, with its
 * An incredibly fast **entity-component system** based on sparse sets, with its
   own _pay for what you use_ policy to adjust performance and memory usage
   own _pay for what you use_ policy to adjust performance and memory usage
   according to the users' requirements.
   according to the users' requirements.
 * Views and groups to iterate entities and components and allow different access
 * Views and groups to iterate entities and components and allow different access
   patterns, from **perfect SoA** to fully random.
   patterns, from **perfect SoA** to fully random.
-* A lot of **facilities** built on top of the entity-component system to support
-  the users and avoid reinventing the wheel (dependencies, snapshot, actor class
-  for those who aren't confident with the architecture and so on).
+* A lot of **facilities** built on top of the entity-component system to help
+  the users and avoid reinventing the wheel (dependencies, snapshot, actor
+  class, support for **reactive systems** and so on).
 * The smallest and most basic implementation of a **service locator** ever seen.
 * The smallest and most basic implementation of a **service locator** ever seen.
-* A built-in, non-intrusive and macro-free **runtime reflection system**.
+* A built-in, non-intrusive and macro-free runtime **reflection system**.
 * A **cooperative scheduler** for processes of any type.
 * A **cooperative scheduler** for processes of any type.
 * All that is needed for **resource management** (cache, loaders, handles).
 * All that is needed for **resource management** (cache, loaders, handles).
-* **Delegates**, **signal handlers** (with built-in support for collectors) and
-  a tiny **event dispatcher** for immediate and delayed events to integrate in
-  loops.
+* Delegates, **signal handlers** (with built-in support for collectors) and a
+  tiny event dispatcher for immediate and delayed events to integrate in loops.
 * A general purpose **event emitter** as a CRTP idiom based class template.
 * A general purpose **event emitter** as a CRTP idiom based class template.
 * And **much more**! Check out the
 * And **much more**! Check out the
   [**wiki**](https://github.com/skypjack/entt/wiki).
   [**wiki**](https://github.com/skypjack/entt/wiki).

+ 0 - 1
TODO

@@ -24,4 +24,3 @@
 * stable component handle that isn't affected by reallocations
 * stable component handle that isn't affected by reallocations
 * multi component registry::remove and some others?
 * multi component registry::remove and some others?
 * built-in support for dual (or N-) buffering
 * built-in support for dual (or N-) buffering
-* reactive systems

+ 102 - 4
docs/md/entity.md

@@ -14,6 +14,7 @@
 * [Vademecum](#vademecum)
 * [Vademecum](#vademecum)
 * [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
 * [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
   * [Observe changes](#observe-changes)
   * [Observe changes](#observe-changes)
+    * [They call me Reactive System](#they-call-me-reactive-system)
   * [Runtime components](#runtime-components)
   * [Runtime components](#runtime-components)
     * [A journey through a plugin](#a-journey-through-a-plugin)
     * [A journey through a plugin](#a-journey-through-a-plugin)
   * [Sorting: is it possible?](#sorting-is-it-possible)
   * [Sorting: is it possible?](#sorting-is-it-possible)
@@ -57,8 +58,9 @@ used mostly in game development.
 
 
 ## A bitset-free entity-component system
 ## A bitset-free entity-component system
 
 
-`EnTT` is a _bitset-free_ entity-component system that doesn't require users to
-specify the component set at compile-time.<br/>
+`EnTT` offers a _bitset-free_ entity-component system that doesn't require users
+to specify the set of components neither at compile-time nor at runtime before
+being able to use the library itself.<br/>
 This is why users can instantiate the core class simply like:
 This is why users can instantiate the core class simply like:
 
 
 ```cpp
 ```cpp
@@ -71,6 +73,10 @@ In place of its more annoying and error-prone counterpart:
 entt::registry<comp_0, comp_1, ..., comp_n> registry;
 entt::registry<comp_0, comp_1, ..., comp_n> registry;
 ```
 ```
 
 
+Furthermore, there is no need to indicate to the library in any way that a type
+of component exists and will be used sooner or later. When the time comes, users
+can just use it and that's all.
+
 ## Pay per use
 ## Pay per use
 
 
 `EnTT` is entirely designed around the principle that users have to pay only for
 `EnTT` is entirely designed around the principle that users have to pay only for
@@ -409,6 +415,90 @@ should be kept to a minimum, if possible. Note also that the greater the number
 of listeners, the greater the performance hit when components are created or
 of listeners, the greater the performance hit when components are created or
 destroyed.
 destroyed.
 
 
+### They call me Reactive System
+
+As mentioned above, signals are the basic tools to construct reactive systems,
+even if they are not enough on their own.<br/>
+`EnTT` tries to take another step in that direction with the `observer` class
+template.
+
+In order to explain what reactive systems are, this is a slightly revised quote
+from the documentation of the library that first introduced this tool,
+[Entitas](https://github.com/sschmid/Entitas-CSharp):
+
+>Imagine you have 100 fighting units on the battlefield but only 10 of them
+>changed their positions. Instead of using a normal system and updating all 100
+>entities depending on the position, you can use a reactive system which will
+>only update the 10 changed units. So efficient.
+
+In `EnTT`, this means to iterating over a reduced set of entities and components
+with respect to what would otherwise be returned from a view or group.<br/>
+On these words, however, the similarities with the proposal of Entitas also end.
+The rules of language and the design of the library obviously impose and allow
+different things.
+
+An `observer` is initialized with an instance of a registry and a set of rules
+that describe what are the entities to intercept. As an example:
+
+```cpp
+entt::observer observer{registry, entt::collector.replace<sprite>()};
+```
+
+The class also default constructible if required and it can be reconfigured at
+any time by means of the `connect` member function. Moreover, instances can be
+disconnected from the underlying registries through the `disconnect` member
+function.<br/>
+The `observer` offers also some member functions to query its internal state and
+to know if it's empty or how many entities it contains. Moreover, it can return
+a raw pointer to the list of entities it contains.<br/>
+However, the most important features of this class are that:
+
+* It's iterable and therefore users can easily walk through the list of entities
+  by means of a range-for loop.
+* It's clearable and therefore users can consume the entities and literally
+  reset the observer after each iteration.
+
+These aspects make the observer an incredibly powerful tool to know at any time
+what are the entities that have started to respect the given rules since the
+last time one asked:
+
+```cpp
+for(const auto entity: observer) {
+    // ...
+}
+
+observer.clear();
+```
+
+The `collector` is an utility to use to generate a list of `matcher`s (the
+actual rules) to use with an `observer`.<br/>
+There are two types of `matcher`s:
+
+* 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.
+
+  ```cpp
+  collector.replace<sprite>();
+  ```
+
+* 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.
+
+  ```cpp
+  collector.group<position, velocity>(entt::exclude<destroyed>);
+  ```
+
+  A grouping matcher supports also exclusion lists as well as single components.
+
+Roughly speaking, an observing matcher intercepts the entities for which the
+given components are replaced (as in `registry::replace`) while a grouping
+matcher tracks the entities that have assigned the given components since the
+last time one asked.<br/>
+Note that, for a grouping matcher, if an entity already has all the components
+except one and the missing type is assigned to it, it is intercepted.
+
 ## Runtime components
 ## Runtime components
 
 
 Defining components at runtime is useful to support plugin systems and mods in
 Defining components at runtime is useful to support plugin systems and mods in
@@ -766,29 +856,37 @@ Creating an entity from a prototype is straightforward:
 
 
 * To create a new entity from scratch and assign it a prototype, this is the way
 * To create a new entity from scratch and assign it a prototype, this is the way
   to go:
   to go:
+
   ```cpp
   ```cpp
   const auto entity = prototype();
   const auto entity = prototype();
   ```
   ```
+
   It is equivalent to the following invokation:
   It is equivalent to the following invokation:
+
   ```cpp
   ```cpp
   const auto entity = prototype.create();
   const auto entity = prototype.create();
   ```
   ```
 
 
-* In case we want to initialize an already existing entity, we can provide the
-  `operator()` directly with the entity identifier:
+* To initialize an already existing entity, users can provide the `operator()`
+  directly with the entity identifier:
+
   ```cpp
   ```cpp
   prototype(entity);
   prototype(entity);
   ```
   ```
+
   It is equivalent to the following invokation:
   It is equivalent to the following invokation:
+
   ```cpp
   ```cpp
   prototype.assign(entity);
   prototype.assign(entity);
   ```
   ```
+
   Note that existing components aren't overwritten in this case. Only those
   Note that existing components aren't overwritten in this case. Only those
   components that the entity doesn't own yet are copied over. All the other
   components that the entity doesn't own yet are copied over. All the other
   components remain unchanged.
   components remain unchanged.
 
 
 * Finally, to assign or replace all the components for an entity, thus
 * Finally, to assign or replace all the components for an entity, thus
   overwriting existing ones:
   overwriting existing ones:
+
   ```cpp
   ```cpp
   prototype.assign_or_replace(entity);
   prototype.assign_or_replace(entity);
   ```
   ```

+ 1 - 1
src/entt/entity/observer.hpp

@@ -267,7 +267,7 @@ public:
      */
      */
     template<typename... Matcher>
     template<typename... Matcher>
     void connect(basic_registry<entity_type> &reg, basic_collector<Matcher...>) {
     void connect(basic_registry<entity_type> &reg, basic_collector<Matcher...>) {
-        release ? release(*target, *this) : void();
+        disconnect();
         connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
         connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
         target = &reg;
         target = &reg;
         view.reset();
         view.reset();