Для того чтобы компоненты могли обновлять каждый кадр (и исключать эту функциональность из компонентов, которые не нужны), у меня появилась идея создать компонент UpdateComponent. Другие компоненты, такие как MovableComponent
(который содержит скорость) будут наследоваться от IUpdatable
абстрактного класса. Это заставляет MovableComponent
реализовать Update(gametime dt)
метод и другой, RegisterWithUpdater()
который дает UpdateComponent
указатель на MovableComponent
. Многие компоненты могут сделать это, а затем UpdateComponent
могут вызывать все свои Update(gametime dt)
методы, не заботясь о том, кто или что они есть.
Мои вопросы:
- Кажется ли это чем-то нормальным или кем-то используемым? Я не могу найти что-нибудь по этому вопросу.
- Как я могу поддерживать порядок для таких компонентов, как физика, а затем менять положение? Это даже необходимо?
- Каковы другие способы обеспечения того, чтобы компоненты, которые должны обрабатываться в каждом кадре, действительно обрабатывались?
РЕДАКТИРОВАТЬ
Я думаю, что я решу, как дать менеджеру сущностей список типов, которые можно обновлять. Тогда ВСЕ компоненты этого типа могут обновлять, а не управлять ими для каждой сущности (которые в любом случае являются просто индексами в моей системе).
По-прежнему. Мои вопросы остаются в силе для меня. Я не знаю, является ли это разумным / нормальным или что другие склонны делать.
Кроме того, люди в Insomniac потрясающие! /РЕДАКТИРОВАТЬ
Код выкипания для предыдущего примера:
class IUpdatable
{
public:
virtual void Update(float dt) = 0;
protected:
virtual void RegisterAsUpdatable() = 0;
};
class Component
{
...
};
class MovableComponent: public Component, public IUpdatable
{
public:
...
virtual void Update(float dt);
private:
...
virtual void RegisterWithUpdater();
};
class UpdateComponent: public Component
{
public:
...
void UpdateAll();
void RegisterUpdatable(Component* component);
void RemoveUpdatable(Component* component);
private:
...
std::set<Component*> updatables_;
};
источник
Ответы:
Одним из основных преимуществ системы компонентов является возможность воспользоваться шаблонами кэширования - хороший icache и прогнозирование, потому что вы выполняете один и тот же код снова и снова, хороший dcache, потому что вы можете размещать объекты в однородных пулах и потому, что виртуальные таблицы, если любой, останься горячим.
То, как вы структурировали свои компоненты, это преимущество полностью исчезает, и фактически может стать причиной снижения производительности по сравнению с системой на основе наследования, поскольку вы делаете гораздо больше виртуальных вызовов и больше объектов с помощью vtables.
Что вы должны делать, это хранить пулы для каждого типа и выполнять итерацию каждого типа независимо для выполнения обновлений.
Это не так часто встречается в больших играх, потому что это не выгодно. Это часто встречается во многих играх, но технически не интересно, поэтому никто не пишет об этом.
Код в таких языках, как C ++, имеет естественный способ упорядочения выполнения: введите операторы в таком порядке.
На самом деле это не имеет смысла, потому что ни одна надежная физическая система не структурирована таким образом - вы не можете обновить один физический объект. Вместо этого код будет выглядеть примерно так:
источник
То, о чем вы говорите, является разумным и довольно распространенным, я думаю. Это может дать вам больше информации.
источник
Мне нравятся эти подходы:
Кратко: избегайте сохранения поведения обновления внутри компонентов. Компоненты не являются поведением. Поведение (включая обновления) может быть реализовано в некоторых подсистемах одного экземпляра. Этот подход также может помочь в пакетной обработке аналогичного поведения для нескольких компонентов (возможно, с использованием инструкций parallel_for или SIMD для данных компонентов).
Метод IUpdatable idea and Update (gametime dt) кажется слишком ограничительным и вводит дополнительные зависимости. Это может быть хорошо, если вы не используете подход подсистем, но если вы используете их, то IUpdatable является избыточным уровнем иерархии. В конце концов, MovingSystem должна знать, что она должна обновлять компоненты Location и / или Velocity напрямую для всех объектов, которые имеют эти компоненты, поэтому нет необходимости в некотором промежуточном компоненте IUpdatable.
Но вы можете использовать компонент Updatable в качестве механизма пропуска обновления некоторых объектов, несмотря на то, что они имеют компоненты Location и / или Velocity. У компонента Updatable может быть флаг bool, который может быть установлен
false
и который будет сигнализировать каждой подсистеме, поддерживающей обновление, о том, что данный конкретный объект в настоящее время не должен обновляться (хотя в таком контексте Freezable представляется более подходящим именем для компонента).источник