- Как сохранить типы, которые будут использоваться в качестве схемы распределения позже?
Поскольку вы знаете типы компонентов во время компиляции, вы можетеиспользуйте псевдоним типа:
template<class... Components>
struct Entities {
/* ... to be implemented ... */
};
using HealthsPositions = Entities<Health, Position>;
Могу ли я запросить, какие типы есть в EntityType?
Да, и это также известно во время компиляции.Похоже, что в пространстве имен std
нет помощника для проверки, содержится ли тип в списке типов (см. Разнообразие ответов на на этот вопрос ).Итак, вот еще один способ решить эту задачу template-metaprogramming в C ++ 14:
template<class Component, class EntitiesCs>
struct IsComponentOf;
template<class Component, class... Cs>
struct IsComponentOf<Component, Entities<Cs...>> {// partial specialization
static constexpr bool value_() {
bool ret = false;
for(bool is_same : {std::is_same<Component, Cs>{}()...}) {
ret |= is_same;
}
return ret;
// C++17 version with fold expression:
// return (... || std::is_same<Component, Cs>{});
}
static constexpr bool value = value_();
constexpr operator bool() const { return value; }
};
static_assert(IsComponentOf<Health, HealthsPositions>{}, "");
static_assert(IsComponentOf<Position, HealthsPositions>{}, "");
static_assert(not IsComponentOf<int, HealthsPositions>{}, "");
Можно ли как-то работать с typeid?
Да, но это альтернативный подход к тому, что я описал выше: вышеописанное работает во время компиляции.Оператор typeid
происходит из мира информации о типах времени выполнения (RTTI).К сожалению, std::type_info
не может использоваться во время компиляции.
Затем я буду использовать этот класс EntityType в качестве образца для выделения плотно упакованной памяти для компонентов.
EntityManager.BatchCreate(3, entityType);
// Result: Health | Health | Health | Position | Position | Position
Если вы действительно хотите, чтобы компоненты были плотно упакованы, и если вы хотите иметь возможность изменять размер «контейнера», то я не вижу простого решения.В идеальном случае HealthsPositions
хранит, например,
- элемент в виде указателя в памяти, где начинается первый компонент
Health
, - a
std::size_t
(илилюбой) элемент, который хранит количество компонентов для каждого типа, и - a
std::size_t
(или любой другой) элемент, который хранит емкость компонентов для каждого типа.
Для этого идеального случая требуетсянекоторое настраиваемое управление памятью (включая вопросы выравнивания).
Однако хорошей отправной точкой может быть альтернативный простой дизайн :
#include <cstddef>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <vector>
struct Health { int amount; };
struct Position { float x; float y; };
template<class C0, class... Cs>
struct Entities {
std::tuple<
std::vector<C0>, std::vector<Cs>...
> components;
Entities(std::size_t size)
: components{size, (0*sizeof(Cs) + size)...}
{}
};
template<class Component, class... Cs>
constexpr std::vector<Component>& get(Entities<Cs...>& e) {
using ComponentVector = std::vector<Component>;
return std::get<ComponentVector>(e.components);
}
template<class Component, class... Cs>
constexpr const std::vector<Component>& get(const Entities<Cs...>& e) {
using ComponentVector = std::vector<Component>;
return std::get<ComponentVector>(e.components);
}
////////////////////////////////////////////////////////////////////////////////
using HealthsPositions = Entities<Health, Position>;
constexpr std::size_t expected_size =
sizeof(std::vector<Health>) + sizeof(std::vector<Position>);
static_assert(sizeof(HealthsPositions) == expected_size, "");
int main() {
std::size_t entity_count = 7;
HealthsPositions hps(entity_count);
get<Health>(hps).at(2).amount = 40;
get<Position>(hps).at(5) = Position{3.5f, 8.4f};
std::cout << "health address and value:\n";
for(auto&& h : get<Health>(hps)) {
std::cout << &h << "\t" << h.amount << "\n";
}
std::cout << "position address and value:\n";
for(auto&& p : get<Position>(hps)) {
std::cout << &p << "\t" << p.x << "\t" << p.y << "\n";
}
}
Пример вывода:
health address and value:
0x55adba092eb0 0
0x55adba092eb4 0
0x55adba092eb8 40
0x55adba092ebc 0
0x55adba092ec0 0
0x55adba092ec4 0
0x55adba092ec8 0
position address and value:
0x55adba092e70 0 0
0x55adba092e78 0 0
0x55adba092e80 0 0
0x55adba092e88 0 0
0x55adba092e90 0 0
0x55adba092e98 3.5 8.4
0x55adba092ea0 0 0