I am designing a compile-time ECS. The design has its specifics - for instance, there will be a great variety of entities. This means that the overall number of components is likely to be fairly high whilst number of components per entity will be fairly low (having some obvious present-almost-everywhere-commons such as Sprite
or BoundingBox
)
I would implement the following interface:
/* Tell the compiler which fields we would use in this entity */
Entity<GraphicsComponent, PhysicsComponent> entity;
/* Populate the handles with the appropriate data (integer, pointer, doesn't matter) */
// via explicit creation
Entity.add<GraphicsComponent>(RenderSystem::create<GraphicsComponent>());
// or in some other way?
The inception of the Entity
class I wrote:
template <typename ...>
class Entity final {};
I have, however, no idea how would I create the handle data fields for the respective Component types , and if it is even possible at all. Can anyone help me or explain why it wouldn't work (or possibly suggest a different solution)?
Just use std::tuple
. Nb the get
requires that each of Components...
is a distinct type
template <typename ... Components>
class Entity final {
std::tuple<Components...> components;
template<typename Component>
void add(Component component) // possibly cv / ref qualified
{
std::get<std::decay_t<Component>>(components) = component;
}
};
Use std::variant
for a mixture of flexibility and simplicity, at the cost of possibly sacrificing some memory.
template<typename... Components>
class Entity final {
using variant = std::variant<Components...>;
std::vector<variant> components;
public:
template<typename T>
void add(T&& t) {
components.push_back(std::forward<T>(t));
}
};
The memory problem arises when your component have largely varying sizes. Say one is 1 byte while another is 200 bytes, all the entries in the std::vector
will be at least 200 bytes large.
Another solution is std::any
.
template<typename... Components>
class Entity final {
std::vector<std::any> components;
public:
template<typename T>
void add(T&& t) {
static_assert((std::is_same_v<Components, std::decay_t<T>> || ...));
components.push_back(std::forward<T>(t));
}
};
std::any
most certainly have everything on the heap except very small objects, but doesn't suffer from the aforementioned problem with std::variant
.
In the case with std::any
, we use the type system only to enable checks, not to govern the layout of data.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.