В настоящее время я делаю программу Entity-Component-System как способ изучения C ++ (я знаю, что это, вероятно, не рекомендуемый способ, но мне весело). Пока все идет хорошо, но я надеюсь сделать еще несколько улучшений.
Каждая система в настоящее время добавляется в диспетчер ECS как std::function
вместе с сигнатурой того, какие компоненты требуется запустить. Моя текущая стратегия заключается в итерации каждой системы и объекта, и если сигнатуры компонентов совпадают, то системная функция вызывается с идентификатором объекта в качестве параметра.
// System function
static Game::runSystem(Manager & mngr, int id) {
ComponentA * a = mngr.getComponent<ComponentA>(id);
ComponentB * b = mngr.getComponent<ComponentB>(id);
// Do something with a and b
}
Эта функция присоединяется к менеджеру с помощью этого метода:
// Definition
template<typename...Args>
void Manager::addSystem(std::function<void(XManager & mngr, int id)> f); // A bitmask signature is generated based on the template
// Use
manager.addSystem<ComponentA, ComponentB>(Game::runSystem);
ComponentA
и ComponentB
- это просто общие типы. Я даже могу иметь компонент float
или int
, если мне нужно. При использовании getComponent<T>()
они приводятся в соответствующие указатели.
Все это работает отлично, но в итоге мне нужно включить файл заголовка для менеджера для каждой системы, и я бы предпочел, чтобы онине должны быть связаны друг с другом (я думаю, это будет разделение интересов).
Желательно, чтобы каждый процесс в конечном итоге выглядел примерно так:
static Game::runSystem(ComponentA * a, ComponentB * b) {
// Do something with a and b
}
Единственное, что понадобится Системезнать, являются ли указатели компонентов для конкретной сущности, над которой он обрабатывается.
В настоящее время я храню компоненты в std::unordered_map
в соответствии с битовой маской его Типа. Когда необходим компонент, шаблон предоставляет тип, из которого я могу сделать битовую маску, а затем наложить ее. Я не уверен, возможно ли сохранить тип компонента для приведения, поэтому я думаю, что каждый компонент должен быть передан в функцию как указатель void. Но C ++ не позволяет мне неявно приводить из void*
к другому указателю, не зная типа. Есть ли способ, которым я могу сохранить Тип каждого компонента для приведения позже, когда это необходимо?
Я думаю, что я могу просто иметь определение функции с void*
для каждого компонента, который требуется системе, но интуитивноэто кажется плохой идеей. Это совсем не безопасно для типов, и я не смогу узнать, какие параметры ему нужны, посмотрев объявление функции.
Как передать ссылку на функцию, которая может принимать любое количество указателей любоготип? Есть ли способ сделать это с помощью шаблонов или я могу неявно привести указатель void, не зная тип? Я открыт и для других стратегий.