deprecated prototype/documented registry::stomp
This commit is contained in:
@@ -18,12 +18,12 @@
|
||||
* [Runtime components](#runtime-components)
|
||||
* [A journey through a plugin](#a-journey-through-a-plugin)
|
||||
* [Sorting: is it possible?](#sorting-is-it-possible)
|
||||
* [Multiple registries to the rescue](#multiple-registries-to-the-rescue)
|
||||
* [Snapshot: complete vs continuous](#snapshot-complete-vs-continuous)
|
||||
* [Snapshot loader](#snapshot-loader)
|
||||
* [Continuous loader](#continuous-loader)
|
||||
* [Archives](#archives)
|
||||
* [One example to rule them all](#one-example-to-rule-them-all)
|
||||
* [Prototype](#prototype)
|
||||
* [The actor class](#the-actor-class)
|
||||
* [Helpers](#helpers)
|
||||
* [Dependency function](#dependency-function)
|
||||
@@ -627,6 +627,56 @@ In fact, there are two functions that respond to slightly different needs:
|
||||
In this case, instances of `movement` are arranged in memory so that cache
|
||||
misses are minimized when the two components are iterated together.
|
||||
|
||||
As a side note, when groups are involved, the sorting functions are applied
|
||||
separately to the elements that are part of the group and to those that are not,
|
||||
effectively generating two partitions, both of which can be ordered
|
||||
independently of each other.
|
||||
|
||||
## Multiple registries to the rescue
|
||||
|
||||
The use of multiple registries is quite common. Examples of use are the
|
||||
separation of the UI from the simulation or the loading of different scenes in
|
||||
the background, possibly on a separate thread, without having to keep track of
|
||||
which entity belongs to which scene.<br/>
|
||||
In fact, with `EnTT` this is even a recommended practice, as the registry is
|
||||
nothing more than a container and different optimizations can be applied to
|
||||
different containers.
|
||||
|
||||
Once there are multiple registries available, however, a method is needed to
|
||||
transfer information from one container to another and this results in the
|
||||
`stomp` member function of the `registry` class.<br/>
|
||||
This function allows to take an entity from a registry and copy it over another
|
||||
entity in another registry (or even in place, actually making a local copy).
|
||||
|
||||
It opens definitely the doors to a lot of interesting features. Below is a
|
||||
brief, yet incomplete list of some of them:
|
||||
|
||||
* Prototypes or templates for high level _concepts_ to use to spawn new entities
|
||||
when needed or to _apply_ a given set of components to an existing
|
||||
entity.<br/>
|
||||
Put aside the fact that having the prototypes separated from the simulation is
|
||||
useful in many cases, they make also the codebase easier to maintain, since
|
||||
updating a template is much less error prone than jumping in the code to
|
||||
update all the snippets copied and pasted around to initialize entities and
|
||||
components.
|
||||
|
||||
* Literally _move_ entities from one registry to another (that is a copy
|
||||
followed by a destroy), which can be useful for solving problems such as the
|
||||
migration of entities between different scenes without loss of information.
|
||||
|
||||
* Even prefabs, shared instances without explicit owner and copy-on-write
|
||||
policies among other things are easily achievable in this way and with the
|
||||
_right_ types in use for the components.
|
||||
|
||||
* Remove entities that are distant or not visible so as to reduce the processing
|
||||
time, moving them to a _cold_ registry from which they can be easily resumed
|
||||
at any time.
|
||||
|
||||
And so on. There are many things that become possible by combining multiple
|
||||
containers but none is really useful without the right means to move entities
|
||||
and components between registries.<br/>
|
||||
Therefore, this seemed a good reason to implement such a feature.
|
||||
|
||||
## Snapshot: complete vs continuous
|
||||
|
||||
The `registry` class offers basic support to serialization.<br/>
|
||||
@@ -866,82 +916,6 @@ the best way to do it. However, feel free to use it at your own risk.
|
||||
The basic idea is to store everything in a group of queues in memory, then bring
|
||||
everything back to the registry with different loaders.
|
||||
|
||||
## Prototype
|
||||
|
||||
A prototype defines a type of an application in terms of its parts. They can be
|
||||
used to assign components to entities of a registry at once.<br/>
|
||||
Roughly speaking, in most cases prototypes can be considered just as templates
|
||||
to use to initialize entities according to _concepts_. In fact, users can create
|
||||
how many prototypes they want, each one initialized differently from the others.
|
||||
|
||||
The following is an example of use of a prototype:
|
||||
|
||||
```cpp
|
||||
entt::registry registry;
|
||||
entt::prototype prototype{registry};
|
||||
|
||||
prototype.set<position>(100.f, 100.f);
|
||||
prototype.set<velocity>(0.f, 0.f);
|
||||
|
||||
// ...
|
||||
|
||||
const auto entity = prototype();
|
||||
```
|
||||
|
||||
To assign and remove components from a prototype, it offers two dedicated member
|
||||
functions named `set` and `unset`. The `has` member function can be used to know
|
||||
if a given prototype contains one or more components and the `get` member
|
||||
function can be used to retrieve the components.
|
||||
|
||||
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();
|
||||
```
|
||||
|
||||
* 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);
|
||||
```
|
||||
|
||||
In the examples above, the prototype uses its underlying registry to create
|
||||
entities and components both for its purposes and when it's cloned. To use a
|
||||
different repository to clone a prototype, all the member functions accept also
|
||||
a reference to a valid registry as a first argument.
|
||||
|
||||
Prototypes are a very useful tool that can save a lot of typing sometimes.
|
||||
Furthermore, the codebase may be easier to maintain, since updating a prototype
|
||||
is much less error prone than jumping around in the codebase to update all the
|
||||
snippets copied and pasted around to initialize entities and components.
|
||||
|
||||
## The actor class
|
||||
|
||||
The `actor` class is designed for those who don't feel immediately comfortable
|
||||
|
||||
@@ -19,6 +19,11 @@ namespace entt {
|
||||
/**
|
||||
* @brief Prototype container for _concepts_.
|
||||
*
|
||||
* @deprecated
|
||||
* This class will be wiped out in a future version of the library.<br/>
|
||||
* Use a shadow registry and the new `registry::stomp` functionality to achieve
|
||||
* the same result in a more idiomatic way.
|
||||
*
|
||||
* A prototype is used to define a _concept_ in terms of components.<br/>
|
||||
* Prototypes act as templates for those specific types of an application which
|
||||
* users would otherwise define through a series of component assignments to
|
||||
|
||||
@@ -205,7 +205,7 @@ class basic_registry {
|
||||
std::unique_ptr<sparse_set<Entity>> pool;
|
||||
void(* remove)(sparse_set<Entity> &, basic_registry &, const Entity);
|
||||
std::unique_ptr<sparse_set<Entity>>(* clone)(const sparse_set<Entity> &);
|
||||
void(* accommodate)(const sparse_set<Entity> &, const Entity, registry &, const Entity);
|
||||
void(* stomp)(const sparse_set<Entity> &, const Entity, registry &, const Entity);
|
||||
ENTT_ID_TYPE runtime_type;
|
||||
};
|
||||
|
||||
@@ -295,12 +295,12 @@ class basic_registry {
|
||||
return std::make_unique<pool_type<Component>>(static_cast<const pool_type<Component> &>(cpool));
|
||||
};
|
||||
|
||||
pdata->accommodate = [](const sparse_set<Entity> &cpool, const Entity from, registry &other, const Entity to) {
|
||||
pdata->stomp = [](const sparse_set<Entity> &cpool, const Entity from, registry &other, const Entity to) {
|
||||
other.assign_or_replace<Component>(to, static_cast<const pool_type<Component> &>(cpool).get(from));
|
||||
};
|
||||
} else {
|
||||
pdata->clone = nullptr;
|
||||
pdata->accommodate = nullptr;
|
||||
pdata->stomp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1496,7 +1496,7 @@ public:
|
||||
auto &curr = other.pools[pos-1];
|
||||
curr.remove = pdata.remove;
|
||||
curr.clone = pdata.clone;
|
||||
curr.accommodate = pdata.accommodate;
|
||||
curr.stomp = pdata.stomp;
|
||||
curr.pool = pdata.clone ? pdata.clone(*pdata.pool) : nullptr;
|
||||
curr.runtime_type = pdata.runtime_type;
|
||||
}
|
||||
@@ -1546,20 +1546,20 @@ public:
|
||||
* @param to A valid entity identifier to copy to.
|
||||
*/
|
||||
template<typename... Component, typename... Exclude>
|
||||
void clone(const Entity from, registry &other, const Entity to, exclude_t<Exclude...> = {}) {
|
||||
void stomp(const Entity from, registry &other, const Entity to, exclude_t<Exclude...> = {}) {
|
||||
static_assert(std::conjunction_v<std::is_copy_constructible<Component>...>);
|
||||
ENTT_ASSERT(valid(from) && other.valid(to));
|
||||
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
const auto &pdata = pools[pos-1];
|
||||
ENTT_ASSERT(!sizeof...(Component) || !pdata.pool || pdata->accommodate);
|
||||
ENTT_ASSERT(!sizeof...(Component) || !pdata.pool || pdata->stomp);
|
||||
|
||||
if(pdata.pool && pdata.accommodate
|
||||
if(pdata.pool && pdata.stomp
|
||||
&& (!sizeof...(Component) || ... || (pdata.runtime_type == to_integer(type<Component>())))
|
||||
&& !((pdata.runtime_type == to_integer(type<Exclude>())) || ...)
|
||||
&& pdata.pool->has(from))
|
||||
{
|
||||
pdata.accommodate(*pdata.pool, from, other, to);
|
||||
pdata.stomp(*pdata.pool, from, other, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user