doc: observer/reactive systems
This commit is contained in:
17
README.md
17
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/>
|
||||
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).
|
||||
* 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.
|
||||
* 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
|
||||
according to the users' requirements.
|
||||
* Views and groups to iterate entities and components and allow different access
|
||||
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.
|
||||
* 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.
|
||||
* 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.
|
||||
* And **much more**! Check out the
|
||||
[**wiki**](https://github.com/skypjack/entt/wiki).
|
||||
|
||||
1
TODO
1
TODO
@@ -24,4 +24,3 @@
|
||||
* stable component handle that isn't affected by reallocations
|
||||
* multi component registry::remove and some others?
|
||||
* built-in support for dual (or N-) buffering
|
||||
* reactive systems
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* [Vademecum](#vademecum)
|
||||
* [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
|
||||
* [Observe changes](#observe-changes)
|
||||
* [They call me Reactive System](#they-call-me-reactive-system)
|
||||
* [Runtime components](#runtime-components)
|
||||
* [A journey through a plugin](#a-journey-through-a-plugin)
|
||||
* [Sorting: is it possible?](#sorting-is-it-possible)
|
||||
@@ -57,8 +58,9 @@ used mostly in game development.
|
||||
|
||||
## 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:
|
||||
|
||||
```cpp
|
||||
@@ -71,6 +73,10 @@ In place of its more annoying and error-prone counterpart:
|
||||
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
|
||||
|
||||
`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
|
||||
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
|
||||
|
||||
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 go:
|
||||
|
||||
```cpp
|
||||
const auto entity = prototype();
|
||||
```
|
||||
|
||||
It is equivalent to the following invokation:
|
||||
|
||||
```cpp
|
||||
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
|
||||
prototype(entity);
|
||||
```
|
||||
|
||||
It is equivalent to the following invokation:
|
||||
|
||||
```cpp
|
||||
prototype.assign(entity);
|
||||
```
|
||||
|
||||
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 remain unchanged.
|
||||
|
||||
* Finally, to assign or replace all the components for an entity, thus
|
||||
overwriting existing ones:
|
||||
|
||||
```cpp
|
||||
prototype.assign_or_replace(entity);
|
||||
```
|
||||
|
||||
@@ -267,7 +267,7 @@ public:
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) {
|
||||
release ? release(*target, *this) : void();
|
||||
disconnect();
|
||||
connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
|
||||
target = ®
|
||||
view.reset();
|
||||
|
||||
Reference in New Issue
Block a user