|
@@ -72,8 +72,8 @@ used mostly in game development.
|
|
|
|
|
|
|
|
## Type-less and bitset-free
|
|
## Type-less and bitset-free
|
|
|
|
|
|
|
|
-`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.<br/>
|
|
|
|
|
|
|
+`EnTT` offers a sparse set based model that doesn't require users to specify the
|
|
|
|
|
+set of components neither at compile-time nor at runtime.<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
|
|
@@ -91,20 +91,14 @@ When the time comes, just use it and that's all.
|
|
|
|
|
|
|
|
## Build your own
|
|
## Build your own
|
|
|
|
|
|
|
|
-`EnTT` is designed as a container that can be used at any time just as a vector
|
|
|
|
|
-or any other tool would be used. It doesn't attempt in any way to take over on
|
|
|
|
|
-the user code base, nor to control its main loop or process scheduling.<br/>
|
|
|
|
|
-Unlike other more or less known models, it makes use of independent pools. This
|
|
|
|
|
-has some advantages and disadvantages. The main purpose is to provide a fully
|
|
|
|
|
-customizable tool, where users have the freedom to define pools and opaque
|
|
|
|
|
-proxies for types with specific requirements.
|
|
|
|
|
-
|
|
|
|
|
-The library provides a default implementation for many things and a mixin model
|
|
|
|
|
-that allows users to completely replace or even just enrich the pool dedicated
|
|
|
|
|
-to one or more components.<br/>
|
|
|
|
|
-The built-in signal support is an example of that: defined as a mixin, it's
|
|
|
|
|
-easily disabled if not needed. Similarly, the storage class has a specialization
|
|
|
|
|
-that shows how everything is customizable down to the smallest detail.
|
|
|
|
|
|
|
+`EnTT` is designed as a container that can be used at any time, just like a
|
|
|
|
|
+vector or any other container. It doesn't attempt in any way to take over on the
|
|
|
|
|
+user codebase, nor to control its main loop or process scheduling.<br/>
|
|
|
|
|
+Unlike other more or less well known models, it also makes use of independent
|
|
|
|
|
+pools that can be extended via _static mixins_. The built-in signal support is
|
|
|
|
|
+an example of this flexible model: defined as a mixin, it's easily disabled if
|
|
|
|
|
+not needed. Similarly, the storage class has a specialization that shows how
|
|
|
|
|
+everything is customizable down to the smallest detail.
|
|
|
|
|
|
|
|
## Pay per use
|
|
## Pay per use
|
|
|
|
|
|
|
@@ -117,8 +111,7 @@ Even worse, some approaches tend to heavily affect other functionalities like
|
|
|
the construction and destruction of components to favor iterations, even when it
|
|
the construction and destruction of components to favor iterations, even when it
|
|
|
isn't strictly required. In fact, slightly worse performance along non-critical
|
|
isn't strictly required. In fact, slightly worse performance along non-critical
|
|
|
paths are the right price to pay to reduce memory usage and have overall better
|
|
paths are the right price to pay to reduce memory usage and have overall better
|
|
|
-perfomance sometimes and I've always wondered why this kind of tools do not
|
|
|
|
|
-leave me the choice.<br/>
|
|
|
|
|
|
|
+perfomance.<br/>
|
|
|
`EnTT` follows a completely different approach. It gets the best out from the
|
|
`EnTT` follows a completely different approach. It gets the best out from the
|
|
|
basic data structures and gives users the possibility to pay more for higher
|
|
basic data structures and gives users the possibility to pay more for higher
|
|
|
performance where needed.
|
|
performance where needed.
|
|
@@ -143,47 +136,42 @@ have succeeded.
|
|
|
|
|
|
|
|
The registry to store, the views and the groups to iterate. That's all.
|
|
The registry to store, the views and the groups to iterate. That's all.
|
|
|
|
|
|
|
|
-An entity (the _E_ of an _ECS_) is an opaque identifier that users should use
|
|
|
|
|
-as-is. Inspecting an identifier isn't recommended since its format can change in
|
|
|
|
|
-future and a registry has all the functionalities to query them out-of-the-box.
|
|
|
|
|
-The type `entt::entity` implements the concept of _entity identifier_.<br/>
|
|
|
|
|
-Components (the _C_ of an _ECS_) must be both move constructible and move
|
|
|
|
|
-assignable. They are list initialized by using the parameters provided to
|
|
|
|
|
-construct the component itself. No need to register components or their types
|
|
|
|
|
-neither with the registry nor with the entity-component system at all.<br/>
|
|
|
|
|
|
|
+The `entt::entity` type implements the concept of _entity identifier_. An entity
|
|
|
|
|
+(the _E_ of an _ECS_) is an opaque element to use as-is. Inspecting it isn't
|
|
|
|
|
+recommended since its format can change in future.<br/>
|
|
|
|
|
+Components (the _C_ of an _ECS_) are both move constructible and move assignable
|
|
|
|
|
+types. No need to register them nor their types.<br/>
|
|
|
Systems (the _S_ of an _ECS_) can be plain functions, functors, lambdas and so
|
|
Systems (the _S_ of an _ECS_) can be plain functions, functors, lambdas and so
|
|
|
on. It's not required to announce them in any case and have no requirements.
|
|
on. It's not required to announce them in any case and have no requirements.
|
|
|
|
|
|
|
|
-The following sections explain in short how to use the entity-component system,
|
|
|
|
|
-the core part of the whole library.<br/>
|
|
|
|
|
-The project is composed of many other classes in addition to those describe
|
|
|
|
|
|
|
+The next sections go into detail on how to use the entity-component system part
|
|
|
|
|
+of the `EnTT` library.<br/>
|
|
|
|
|
+The project is composed of many other classes in addition to those described
|
|
|
below. For more details, please refer to the inline documentation.
|
|
below. For more details, please refer to the inline documentation.
|
|
|
|
|
|
|
|
# Pools
|
|
# Pools
|
|
|
|
|
|
|
|
-In `EnTT`, pools of components are made available through a specialized version
|
|
|
|
|
-of a sparse set.
|
|
|
|
|
-
|
|
|
|
|
-Each pool contains all the instances of a single component and all the entities
|
|
|
|
|
-to which it's assigned. Sparse arrays are _paged_ to avoid wasting memory in
|
|
|
|
|
-some cases. Packed arrays of components are also paged to have pointer stability
|
|
|
|
|
-upon additions. Packed arrays of entities are not instead.<br/>
|
|
|
|
|
-All pools can rearrange their items in order to keep the internal arrays tightly
|
|
|
|
|
-packed and maximize performance.
|
|
|
|
|
|
|
+Pools of components are a sort of _specialized version_ of a sparse set. Each
|
|
|
|
|
+pool contains all the instances of a single component type and all the entities
|
|
|
|
|
+to which it's assigned.<br/>
|
|
|
|
|
+Sparse arrays are _paged_ to avoid wasting memory. Packed arrays of components
|
|
|
|
|
+are also paged to have pointer stability upon additions. Packed arrays of
|
|
|
|
|
+entities are not instead.<br/>
|
|
|
|
|
+All pools rearranges their items in order to keep the internal arrays tightly
|
|
|
|
|
+packed and maximize performance, unless pointer stability is enabled.
|
|
|
|
|
|
|
|
# The Registry, the Entity and the Component
|
|
# The Registry, the Entity and the Component
|
|
|
|
|
|
|
|
-A registry can store and manage entities, as well as create views and groups to
|
|
|
|
|
-iterate the underlying data structures.<br/>
|
|
|
|
|
|
|
+A registry stores and manages entities (or better, identifiers) and pools.<br/>
|
|
|
The class template `basic_registry` lets users decide what's the preferred type
|
|
The class template `basic_registry` lets users decide what's the preferred type
|
|
|
to represent an entity. Because `std::uint32_t` is large enough for almost all
|
|
to represent an entity. Because `std::uint32_t` is large enough for almost all
|
|
|
-the cases, there exists also the enum class `entt::entity` that _wraps_ it and
|
|
|
|
|
|
|
+the cases, there also exists the enum class `entt::entity` that _wraps_ it and
|
|
|
the alias `entt::registry` for `entt::basic_registry<entt::entity>`.
|
|
the alias `entt::registry` for `entt::basic_registry<entt::entity>`.
|
|
|
|
|
|
|
|
Entities are represented by _entity identifiers_. An entity identifier contains
|
|
Entities are represented by _entity identifiers_. An entity identifier contains
|
|
|
information about the entity itself and its version.<br/>
|
|
information about the entity itself and its version.<br/>
|
|
|
-User defined identifiers can be introduced through enum classes and class types
|
|
|
|
|
-that define an `entity_type` member of type `std::uint32_t` or `std::uint64_t`.
|
|
|
|
|
|
|
+User defined identifiers are allowed as enum classes and class types that define
|
|
|
|
|
+an `entity_type` member of type `std::uint32_t` or `std::uint64_t`.
|
|
|
|
|
|
|
|
A registry is used both to construct and to destroy entities:
|
|
A registry is used both to construct and to destroy entities:
|
|
|
|
|
|
|
@@ -195,9 +183,9 @@ auto entity = registry.create();
|
|
|
registry.destroy(entity);
|
|
registry.destroy(entity);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-The `create` member function accepts also a hint and has an overload that gets
|
|
|
|
|
|
|
+The `create` member function also accepts a hint and has an overload that gets
|
|
|
two iterators and can be used to generate multiple entities at once efficiently.
|
|
two iterators and can be used to generate multiple entities at once efficiently.
|
|
|
-Similarly, the `destroy` member function works also with a range of entities:
|
|
|
|
|
|
|
+Similarly, the `destroy` member function also works with a range of entities:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
|
// destroys all the entities in a range
|
|
// destroys all the entities in a range
|
|
@@ -207,7 +195,7 @@ registry.destroy(view.begin(), view.end());
|
|
|
|
|
|
|
|
In addition to offering an overload to force the version upon destruction. Note
|
|
In addition to offering an overload to force the version upon destruction. Note
|
|
|
that this function removes all components from an entity before releasing its
|
|
that this function removes all components from an entity before releasing its
|
|
|
-identifier. There exists also a _lighter_ alternative that only releases the
|
|
|
|
|
|
|
+identifier. There also exists a _lighter_ alternative that only releases the
|
|
|
elements without poking in any pool, for use with orphaned entities:
|
|
elements without poking in any pool, for use with orphaned entities:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
@@ -366,15 +354,12 @@ the component owned by an entity if any, a null pointer otherwise.
|
|
|
|
|
|
|
|
## Observe changes
|
|
## Observe changes
|
|
|
|
|
|
|
|
-Because of how the registry works internally, it stores a bunch of signal
|
|
|
|
|
-handlers for each pool in order to notify some of its data structures on the
|
|
|
|
|
-construction and destruction of components or when an instance of a component is
|
|
|
|
|
-explicitly replaced by the user.<br/>
|
|
|
|
|
-These signal handlers are also exposed and made available to users. These are
|
|
|
|
|
-the basic bricks to build fancy things like dependencies and reactive systems.
|
|
|
|
|
|
|
+By default, each storage comes with a mixin that adds signal support to it.<br/>
|
|
|
|
|
+This allows for fancy things like dependencies and reactive systems.
|
|
|
|
|
|
|
|
-To get a sink to be used to connect and disconnect listeners so as to be
|
|
|
|
|
-notified on the creation of a component, use the `on_construct` member function:
|
|
|
|
|
|
|
+The `on_construct` member function returns a _sink_ (which is an object for
|
|
|
|
|
+connecting and disconnecting listeners) for those interested in notifications
|
|
|
|
|
+when a new instance of a given component type is created:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
|
// connects a free function
|
|
// connects a free function
|
|
@@ -390,15 +375,12 @@ registry.on_construct<position>().disconnect<&my_free_function>();
|
|
|
registry.on_construct<position>().disconnect<&my_class::member>(instance);
|
|
registry.on_construct<position>().disconnect<&my_class::member>(instance);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-To be notified when a component is destroyed, use the `on_destroy` member
|
|
|
|
|
-function instead. Finally, the `on_update` member function will return a sink
|
|
|
|
|
-to which to connect listeners to observe changes.<br/>
|
|
|
|
|
-In the last case, given the way C++ works, it's also necessary to use specific
|
|
|
|
|
-member functions to allow the signal to be triggered. In particular, listeners
|
|
|
|
|
-attached to `on_update` will only be invoked following a call to `replace` or
|
|
|
|
|
-`patch`.
|
|
|
|
|
|
|
+Similarly, `on_destroy` and `on_update` are used to receive notifications about
|
|
|
|
|
+the destruction and update of an instance, respectively.<br/>
|
|
|
|
|
+Because of how C++ works, listeners attached to `on_update` are only invoked
|
|
|
|
|
+following a call to `replace`, `emplace_or_replace` or `patch`.
|
|
|
|
|
|
|
|
-The function type of a listener should be equivalent to the following:
|
|
|
|
|
|
|
+The function type of a listener is equivalent to the following:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
|
void(entt::registry &, entt::entity);
|
|
void(entt::registry &, entt::entity);
|
|
@@ -432,17 +414,8 @@ There are also some limitations on what a listener can and cannot do:
|
|
|
intended to provide users with an easy way to perform cleanup and nothing
|
|
intended to provide users with an easy way to perform cleanup and nothing
|
|
|
more.
|
|
more.
|
|
|
|
|
|
|
|
-To a certain extent, these limitations don't apply. However, it's risky to try
|
|
|
|
|
-to force them and users should respect the limitations unless they know exactly
|
|
|
|
|
-what they are doing.
|
|
|
|
|
-
|
|
|
|
|
-Events and therefore listeners must not be used as replacements for systems.
|
|
|
|
|
-They shouldn't contain much logic and interactions with a registry should be
|
|
|
|
|
-kept to a minimum. Moreover, the greater the number of listeners, the greater
|
|
|
|
|
-the performance hit when components are created or destroyed.
|
|
|
|
|
-
|
|
|
|
|
-Please, refer to the documentation of the signal class to know all the features
|
|
|
|
|
-it offers.<br/>
|
|
|
|
|
|
|
+Please, refer to the documentation of the signal class to know about all the
|
|
|
|
|
+features it offers.<br/>
|
|
|
There are many useful but less known functionalities that aren't described here,
|
|
There are many useful but less known functionalities that aren't described here,
|
|
|
such as the connection objects or the possibility to attach listeners with a
|
|
such as the connection objects or the possibility to attach listeners with a
|
|
|
list of parameters that is shorter than that of the signal itself.
|
|
list of parameters that is shorter than that of the signal itself.
|
|
@@ -566,11 +539,10 @@ one.
|
|
|
|
|
|
|
|
## Sorting: is it possible?
|
|
## Sorting: is it possible?
|
|
|
|
|
|
|
|
-Sorting entities and components is possible with `EnTT`. In particular, it's
|
|
|
|
|
-feasible with an in-place algorithm that doesn't require memory allocations nor
|
|
|
|
|
-anything else and is therefore particularly convenient.<br/>
|
|
|
|
|
-With this in mind, there are two functions that respond to slightly different
|
|
|
|
|
-needs:
|
|
|
|
|
|
|
+Sorting entities and components is possible with `EnTT`. In particular, it uses
|
|
|
|
|
+an in-place algorithm that doesn't require memory allocations nor anything else
|
|
|
|
|
+and is therefore particularly convenient.<br/>
|
|
|
|
|
+There are two functions that respond to slightly different needs:
|
|
|
|
|
|
|
|
* Components can be sorted either directly:
|
|
* Components can be sorted either directly:
|
|
|
|
|
|
|
@@ -607,12 +579,11 @@ components. Refer to the specific documentation for more details.
|
|
|
## Helpers
|
|
## Helpers
|
|
|
|
|
|
|
|
The so called _helpers_ are small classes and functions mainly designed to offer
|
|
The so called _helpers_ are small classes and functions mainly designed to offer
|
|
|
-built-in support for the most basic functionalities.<br/>
|
|
|
|
|
-The list of helpers will grow longer as time passes and new ideas come out.
|
|
|
|
|
|
|
+built-in support for the most basic functionalities.
|
|
|
|
|
|
|
|
### Null entity
|
|
### Null entity
|
|
|
|
|
|
|
|
-In `EnTT`, the `entt::null` variable models the concept of _null entity_.<br/>
|
|
|
|
|
|
|
+The `entt::null` variable models the concept of _null entity_.<br/>
|
|
|
The library guarantees that the following expression always returns false:
|
|
The library guarantees that the following expression always returns false:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
@@ -646,11 +617,11 @@ used to create a null entity.
|
|
|
|
|
|
|
|
### Tombstone
|
|
### Tombstone
|
|
|
|
|
|
|
|
-In addition to the null entity, `EnTT` also models the concept of _tombstone_
|
|
|
|
|
-with the `entt::tombstone` variable.<br/>
|
|
|
|
|
|
|
+Similar to the null entity, the `entt::tombstone` variable models the concept of
|
|
|
|
|
+_tombstone_.<br/>
|
|
|
Once created, the integral form of the two values is the same, although they
|
|
Once created, the integral form of the two values is the same, although they
|
|
|
-affect different parts of an identifier. In fact, the tombstone uses only the
|
|
|
|
|
-version part and is completely transparent to the entity part.
|
|
|
|
|
|
|
+affect different parts of an identifier. In fact, the tombstone only uses the
|
|
|
|
|
+version part of it and is completely transparent to the entity part.
|
|
|
|
|
|
|
|
Also in this case, the following expression always returns false:
|
|
Also in this case, the following expression always returns false:
|
|
|
|
|
|
|
@@ -727,9 +698,9 @@ purpose.
|
|
|
|
|
|
|
|
### Invoke
|
|
### Invoke
|
|
|
|
|
|
|
|
-Sometimes it's useful to be able to directly invoke a member function of a
|
|
|
|
|
-component as a callback. It's already possible in practice but requires users to
|
|
|
|
|
-_extend_ their classes and this may not always be possible.<br/>
|
|
|
|
|
|
|
+Sometimes it's useful to directly invoke a member function of a component as a
|
|
|
|
|
+callback. It's already possible in practice but requires users to _extend_ their
|
|
|
|
|
+classes and this may not always be possible.<br/>
|
|
|
The `invoke` helper allows to _propagate_ the signal in these cases:
|
|
The `invoke` helper allows to _propagate_ the signal in these cases:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
@@ -777,17 +748,13 @@ users might want to consider using handles, either const or non-const.
|
|
|
|
|
|
|
|
### Organizer
|
|
### Organizer
|
|
|
|
|
|
|
|
-The `organizer` class template offers minimal support (but sufficient in many
|
|
|
|
|
-cases) for creating an execution graph from functions and their requirements on
|
|
|
|
|
-resources.<br/>
|
|
|
|
|
|
|
+The `organizer` class template offers support for creating an execution graph
|
|
|
|
|
+from a set of functions and their requirements on resources.<br/>
|
|
|
The resulting tasks aren't executed in any case. This isn't the goal of this
|
|
The resulting tasks aren't executed in any case. This isn't the goal of this
|
|
|
tool. Instead, they are returned to the user in the form of a graph that allows
|
|
tool. Instead, they are returned to the user in the form of a graph that allows
|
|
|
for safe execution.
|
|
for safe execution.
|
|
|
|
|
|
|
|
-The functions are added in order of execution to the organizer. Free functions
|
|
|
|
|
-and member functions are supported as template parameters, however there is also
|
|
|
|
|
-the possibility to pass pointers to free functions or decayed lambdas as
|
|
|
|
|
-parameters to the `emplace` member function:
|
|
|
|
|
|
|
+All functions are added in order of execution to the organizer:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
|
entt::organizer organizer;
|
|
entt::organizer organizer;
|
|
@@ -803,23 +770,15 @@ organizer.emplace<&clazz::member_function>(&instance);
|
|
|
organizer.emplace(+[](const void *, entt::registry &) { /* ... */ });
|
|
organizer.emplace(+[](const void *, entt::registry &) { /* ... */ });
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-As for free functions and member functions, these are the parameters that can be
|
|
|
|
|
-presented by their function types and that will be correctly handled:
|
|
|
|
|
|
|
+These are the parameters that a free function or a member function can accept:
|
|
|
|
|
|
|
|
-* A possibly constant reference to a registry. The one passed to the task when
|
|
|
|
|
- it's run will also be passed to the function as-is.
|
|
|
|
|
-
|
|
|
|
|
-* An `entt::view` with any possible combination of types. It will be created
|
|
|
|
|
- from the registry passed to the task and supplied directly to the function.
|
|
|
|
|
-
|
|
|
|
|
-* A possibly constant reference to any type `T`. It will be interpreted as
|
|
|
|
|
- context variable, which will be created within the registry and passed to the
|
|
|
|
|
- function.
|
|
|
|
|
|
|
+* A possibly constant reference to a registry.
|
|
|
|
|
+* An `entt::basic_view` with any possible combination of types.
|
|
|
|
|
+* A possibly constant reference to any type `T` (that is, a context variable)
|
|
|
|
|
|
|
|
The function type for free functions and decayed lambdas passed as parameters to
|
|
The function type for free functions and decayed lambdas passed as parameters to
|
|
|
-`emplace` is `void(const void *, entt::registry &)` instead. The registry is the
|
|
|
|
|
-same as provided to the task. The first parameter is an optional pointer to user
|
|
|
|
|
-defined data to provide upon registration:
|
|
|
|
|
|
|
+`emplace` is `void(const void *, entt::registry &)` instead. The first parameter
|
|
|
|
|
+is an optional pointer to user defined data to provide upon registration:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
|
clazz instance;
|
|
clazz instance;
|
|
@@ -914,7 +873,7 @@ use the preferred tool.
|
|
|
Each registry has a _context_ associated with it, which is an `any` object map
|
|
Each registry has a _context_ associated with it, which is an `any` object map
|
|
|
accessible by type for convenience.<br/>
|
|
accessible by type for convenience.<br/>
|
|
|
The context is returned via the `ctx` functions and offers a minimal set of
|
|
The context is returned via the `ctx` functions and offers a minimal set of
|
|
|
-features including the following:
|
|
|
|
|
|
|
+feature including the following:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
|
// creates a new context variable initialized with the given values
|
|
// creates a new context variable initialized with the given values
|
|
@@ -987,25 +946,16 @@ this reason, a _stable_ deletion method is also offered. This one is such that
|
|
|
the position of the elements is preserved by creating tombstones upon deletion
|
|
the position of the elements is preserved by creating tombstones upon deletion
|
|
|
rather than trying to fill the holes that are created.
|
|
rather than trying to fill the holes that are created.
|
|
|
|
|
|
|
|
-For performance reasons, `EnTT` will also favor storage compaction in all cases,
|
|
|
|
|
-although often accessing a component occurs mostly randomly or traversing pools
|
|
|
|
|
-in a non-linear order on the user side (as in the case of a hierarchy).<br/>
|
|
|
|
|
|
|
+For performance reasons, `EnTT` favors storage compaction in all cases, although
|
|
|
|
|
+often accessing a component occurs mostly randomly or traversing pools in a
|
|
|
|
|
+non-linear order on the user side (as in the case of a hierarchy).<br/>
|
|
|
In other words, pointer stability is not automatic but is enabled on request.
|
|
In other words, pointer stability is not automatic but is enabled on request.
|
|
|
|
|
|
|
|
### In-place delete
|
|
### In-place delete
|
|
|
|
|
|
|
|
-By default, `EnTT` keeps all pools compact when a component is removed. This is
|
|
|
|
|
-done through a swap-and-pop between the removed item and the one occupying the
|
|
|
|
|
-last position in the storage.<br/>
|
|
|
|
|
-Unfortunately, this also inevitably leads the components to change position
|
|
|
|
|
-within the storage, making direct access almost impossible (be it via pointer or
|
|
|
|
|
-index).
|
|
|
|
|
-
|
|
|
|
|
-However, the underlying model with its independent pools helps introduce storage
|
|
|
|
|
-with different deletion policies. In particular, the library offers out of the
|
|
|
|
|
-box support for in-place deletion, thus offering storage with completely stable
|
|
|
|
|
-pointers.<br/>
|
|
|
|
|
-This is done by specializing the `component_traits` class. The compile-time
|
|
|
|
|
|
|
+The library offers out of the box support for in-place deletion, thus offering
|
|
|
|
|
+storage with completely stable pointers.<br/>
|
|
|
|
|
+This is achieved by specializing the `component_traits` class. The compile-time
|
|
|
definition common to all components is the following:
|
|
definition common to all components is the following:
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
@@ -1032,11 +982,10 @@ struct entt::component_traits<position>: basic_component_traits {
|
|
|
This will ensure in-place deletion for the `position` component without further
|
|
This will ensure in-place deletion for the `position` component without further
|
|
|
user intervention.<br/>
|
|
user intervention.<br/>
|
|
|
Views and groups adapt accordingly when they detect a storage with a different
|
|
Views and groups adapt accordingly when they detect a storage with a different
|
|
|
-deletion policy than the default. No specific action is required from the user
|
|
|
|
|
-once in-place deletion is enabled. In particular:
|
|
|
|
|
|
|
+deletion policy than the default. In particular:
|
|
|
|
|
|
|
|
* Groups are incompatible with stable storage and will even refuse to compile.
|
|
* Groups are incompatible with stable storage and will even refuse to compile.
|
|
|
-* Multi type views are completely transparent to storage policies.
|
|
|
|
|
|
|
+* Multi type and runtime views are completely transparent to storage policies.
|
|
|
* Single type views for stable storage types offer the same interface of multi
|
|
* Single type views for stable storage types offer the same interface of multi
|
|
|
type views. For example, only `size_hint` is available.
|
|
type views. For example, only `size_hint` is available.
|
|
|
|
|
|
|
@@ -1084,12 +1033,7 @@ struct entt::component_traits<transform>: basic_component_traits {
|
|
|
Furthermore, it's quite common for a group of elements to be created close in
|
|
Furthermore, it's quite common for a group of elements to be created close in
|
|
|
time and therefore fallback into adjacent positions, thus favoring locality even
|
|
time and therefore fallback into adjacent positions, thus favoring locality even
|
|
|
on random accesses. Locality that won't be sacrificed over time given the
|
|
on random accesses. Locality that won't be sacrificed over time given the
|
|
|
-stability of storage positions, with undoubted performance advantages.<br/>
|
|
|
|
|
-Of course, the cost moves to linear iterations, where views and groups will have
|
|
|
|
|
-to identify (and discard) all tombstones. However, once considered the benefits,
|
|
|
|
|
-from performance to ease of use, and given the many optimizations that make this
|
|
|
|
|
-cost negligible, this is configured as one of the most convenient solutions and
|
|
|
|
|
-certainly something to take into consideration.
|
|
|
|
|
|
|
+stability of storage positions, with undoubted performance advantages.
|
|
|
|
|
|
|
|
## Making the most of range-destroy
|
|
## Making the most of range-destroy
|
|
|
|
|
|
|
@@ -1133,16 +1077,11 @@ out of it.
|
|
|
|
|
|
|
|
## Meet the runtime
|
|
## Meet the runtime
|
|
|
|
|
|
|
|
-`EnTT` takes full advantage of what the language offers at compile-time.<br/>
|
|
|
|
|
-However, this can have its downsides (well known to those familiar with type
|
|
|
|
|
-erasure techniques).
|
|
|
|
|
-
|
|
|
|
|
-To bridge this gap, the library provides a bunch of utilities and features that
|
|
|
|
|
-can be very useful when needed.<br/>
|
|
|
|
|
-On the one hand, storage classes are standalone objects that allow users to work
|
|
|
|
|
-with components within certain limits and without knowing the actual types. On
|
|
|
|
|
-the other hand, it's possible to create component storage dynamically and link
|
|
|
|
|
-them to a name rather than a type.
|
|
|
|
|
|
|
+`EnTT` takes advantage of what the language offers at compile-time. However,
|
|
|
|
|
+this can have its downsides (well known to those familiar with type erasure
|
|
|
|
|
+techniques).<br/>
|
|
|
|
|
+To fill the gap, the library also provides a bunch of utilities and feature that
|
|
|
|
|
+can be very useful to handle types and pools at runtime.
|
|
|
|
|
|
|
|
### A base class to rule them all
|
|
### A base class to rule them all
|
|
|
|
|
|
|
@@ -1150,7 +1089,7 @@ Storage classes are fully self-contained types. These can be extended via mixins
|
|
|
to add more functionalities (generic or type specific). In addition, they offer
|
|
to add more functionalities (generic or type specific). In addition, they offer
|
|
|
a basic set of functions that already allow users to go very far.<br/>
|
|
a basic set of functions that already allow users to go very far.<br/>
|
|
|
The aim is to limit the need for customizations as much as possible, offering
|
|
The aim is to limit the need for customizations as much as possible, offering
|
|
|
-what is usually necessary for the majority of cases.
|
|
|
|
|
|
|
+what is usually necessary for the vast majority of cases.
|
|
|
|
|
|
|
|
When a storage is used through its base class (i.e. when its actual type isn't
|
|
When a storage is used through its base class (i.e. when its actual type isn't
|
|
|
known), there is always the possibility of receiving a `type_info` describing
|
|
known), there is always the possibility of receiving a `type_info` describing
|
|
@@ -1219,8 +1158,8 @@ However, this doesn't work well for users who want to create multiple storage of
|
|
|
the same type associated with different _names_, such as for interacting with a
|
|
the same type associated with different _names_, such as for interacting with a
|
|
|
scripting system.
|
|
scripting system.
|
|
|
|
|
|
|
|
-Nowadays, the library has _solved_ this limitation and offers the possibility of
|
|
|
|
|
-associating a _type_ with a name (or rather, a numeric identifier):
|
|
|
|
|
|
|
+Nowadays, the library has solved this problem and offers the possibility of
|
|
|
|
|
+associating a type with a _name_ (or rather, a numeric identifier):
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
|
using namespace entt::literals;
|
|
using namespace entt::literals;
|
|
@@ -1251,7 +1190,7 @@ _reach_ manually created ones:
|
|
|
registry.destroy(entity);
|
|
registry.destroy(entity);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-Finally, a storage of this type can be used with any view (which also accept
|
|
|
|
|
|
|
+Finally, a storage of this type can be used with any view (which also accepts
|
|
|
multiple storages of the same type, if necessary):
|
|
multiple storages of the same type, if necessary):
|
|
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
@@ -2108,9 +2047,8 @@ is further reduced in most cases.
|
|
|
|
|
|
|
|
### More performance, more constraints
|
|
### More performance, more constraints
|
|
|
|
|
|
|
|
-Groups are a (much) faster alternative to views. However, the higher the
|
|
|
|
|
-performance, the greater the constraints on what is allowed and what is
|
|
|
|
|
-not.<br/>
|
|
|
|
|
|
|
+Groups are a faster alternative to views. However, the higher the performance,
|
|
|
|
|
+the greater the constraints on what is allowed and what is not.<br/>
|
|
|
In particular, groups add in some rare cases a limitation on the creation of
|
|
In particular, groups add in some rare cases a limitation on the creation of
|
|
|
components during iterations. It happens in quite particular cases. Given the
|
|
components during iterations. It happens in quite particular cases. Given the
|
|
|
nature and the scope of the groups, it isn't something in which it will happen
|
|
nature and the scope of the groups, it isn't something in which it will happen
|
|
@@ -2155,33 +2093,22 @@ the same types for which _empty base optimization_ (EBO) is possibile.<br/>
|
|
|
performance and memory usage. However, this also has consequences that are worth
|
|
performance and memory usage. However, this also has consequences that are worth
|
|
|
mentioning.
|
|
mentioning.
|
|
|
|
|
|
|
|
-When an empty type is detected, it's not instantiated in any case. Therefore,
|
|
|
|
|
-only the entities to which it's assigned are made available.<br/>
|
|
|
|
|
-There doesn't exist a way to _get_ empty types from a registry, views and groups
|
|
|
|
|
-will never return instances for them (for example, during a call to `each`) and
|
|
|
|
|
-some functions such as `try_get` or the raw access to the list of components
|
|
|
|
|
-aren't available for empty types. Finally, the `sort` functionality will only
|
|
|
|
|
-accepts callbacks that require to return entities rather than components:
|
|
|
|
|
-
|
|
|
|
|
-```cpp
|
|
|
|
|
-registry.sort<empty_type>([](const entt::entity lhs, const entt::entity rhs) {
|
|
|
|
|
- return entt::registry::entity(lhs) < entt::registry::entity(rhs);
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
|
|
+When an empty type is detected, it's not instantiated by default. Therefore,
|
|
|
|
|
+only the entities to which it's assigned are made available. There doesn't exist
|
|
|
|
|
+a way to _get_ empty types from a registry. Views and groups will never return
|
|
|
|
|
+their instances (for example, during a call to `each`).<br/>
|
|
|
On the other hand, iterations are faster because only the entities to which the
|
|
On the other hand, iterations are faster because only the entities to which the
|
|
|
type is assigned are considered. Moreover, less memory is used, mainly because
|
|
type is assigned are considered. Moreover, less memory is used, mainly because
|
|
|
there doesn't exist any instance of the component, no matter how many entities
|
|
there doesn't exist any instance of the component, no matter how many entities
|
|
|
it is assigned to.
|
|
it is assigned to.
|
|
|
|
|
|
|
|
-More in general, none of the features offered by the library is affected, but
|
|
|
|
|
-for the ones that require to return actual instances.<br/>
|
|
|
|
|
|
|
+More in general, none of the feature offered by the library is affected, but for
|
|
|
|
|
+the ones that require to return actual instances.<br/>
|
|
|
This optimization can be disabled for the whole application by defining the
|
|
This optimization can be disabled for the whole application by defining the
|
|
|
`ENTT_NO_ETO` macro. In this case, empty types will be treated like all other
|
|
`ENTT_NO_ETO` macro. In this case, empty types will be treated like all other
|
|
|
-types, no matter what.<br/>
|
|
|
|
|
-Otherwise, users can specialize the `component_traits` template class and in
|
|
|
|
|
-particular the `ignore_if_empty` alias, disabling this optimization for some
|
|
|
|
|
-types only.
|
|
|
|
|
|
|
+types. Otherwise, users can also specialize the `component_traits` template
|
|
|
|
|
+class and in particular the `ignore_if_empty` alias, disabling this optimization
|
|
|
|
|
+for some types only.
|
|
|
|
|
|
|
|
# Multithreading
|
|
# Multithreading
|
|
|
|
|
|
|
@@ -2280,7 +2207,7 @@ generated from a const registry.
|
|
|
|
|
|
|
|
Fortunately, there is also a way to instantiate storage classes early when in
|
|
Fortunately, there is also a way to instantiate storage classes early when in
|
|
|
doubt or when there are special requirements.<br/>
|
|
doubt or when there are special requirements.<br/>
|
|
|
-Calling the `prepare` method is equivalent to _announcing_ the existence of a
|
|
|
|
|
|
|
+Calling the `storage` method is equivalent to _announcing_ the existence of a
|
|
|
particular storage, to avoid running into problems. For those interested, there
|
|
particular storage, to avoid running into problems. For those interested, there
|
|
|
are also alternative approaches, such as a single threaded tick for the registry
|
|
are also alternative approaches, such as a single threaded tick for the registry
|
|
|
warm-up, but these are not always applicable.<br/>
|
|
warm-up, but these are not always applicable.<br/>
|