Производительность игрового цикла и компонентный подход - PullRequest
3 голосов
/ 21 апреля 2010

У меня есть идея организовать игровой цикл. У меня есть некоторые сомнения по поводу производительности. Может быть, есть лучшие способы сделать что-то.

Предположим, у вас есть массив игровых компонентов. Все они призваны делать что-то на каждой итерации игрового цикла. Например:

GameData data; // shared
app.registerComponent("AI", ComponentAI(data) );
app.registerComponent("Logic", ComponentGameLogic(data) );
app.registerComponent("2d", Component2d(data) );
app.registerComponent("Menu", ComponentMenu(data) )->setActive(false);
//...
while (ok)
{
//...
app.runAllComponents();
//...
}

Преимущества:

  1. хорошее компонентное приложение, без зависимостей, хорошая модульность
  2. мы можем динамически активировать / деактивировать, регистрировать / отменять регистрацию компонентов
  3. некоторые компоненты могут быть прозрачно удалены или заменены, и система по-прежнему будет работать, поскольку ничего не произошло (смените 2d на 3d) (работа в команде: каждый программист создает свои собственные компоненты и не требует, чтобы другие компоненты компилировали код)

Сомнения:

  1. внутренний цикл в игровом цикле с виртуальными вызовами Component :: run ()
  2. Я бы хотел, чтобы Component :: run () возвращал значение bool и проверял это значение. Если возвращается false, компонент должен быть деактивирован. Таким образом, внутренний цикл становится дороже.

Ну, насколько хорошо это решение? Вы использовали это в реальных проектах?

Ответы [ 4 ]

5 голосов
/ 21 апреля 2010

У некоторых программистов на C ++ слишком много опасений по поводу накладных расходов на виртуальные функции. Затраты на виртуальный вызов обычно незначительны по сравнению с тем, что делает функция. Булева проверка тоже не очень дорога.

Делайте все, что приведет к простоте в обслуживании кода. Оптимизируйте позже, только если вам нужно это сделать. Если вы обнаружите, что вам нужно оптимизировать, удаление виртуальных вызовов, вероятно, не будет той оптимизацией, которая вам нужна.

4 голосов
/ 21 апреля 2010

В большинстве "настоящих" игр существуют довольно строгие требования к взаимозависимости между компонентами, и порядок имеет значение.

Это может повлиять или не повлиять на вас, но часто важно, чтобы физика вступила в силу до (или после) обработки взаимодействия с пользователем, в зависимости от вашего сценария и т. Д. В этой ситуации вам может потребоваться дополнительная обработка для правильного упорядочения. .

Кроме того, поскольку у вас, скорее всего, будет какая-то форма графа сцены или пространственное разделение, вы должны убедиться, что ваши «компоненты» также могут воспользоваться этим. Это, вероятно, означает, что, учитывая ваше текущее описание, вы будете ходить по своему дереву слишком много раз. Опять же, однако, это можно обойти с помощью дизайнерских решений. При этом некоторые компоненты могут быть заинтересованы только в определенных частях пространственного раздела, и, опять же, вы захотите спроектировать их соответствующим образом.

1 голос
/ 23 апреля 2010

Может быть, я старшеклассник, но я действительно не вижу значения в универсальных компонентах, потому что не вижу их замены во время выполнения.

struct GameObject
{
   Ai* ai;
   Transform* transform;
   Renderable* renderable;
   Collision* collision;
   Health* health;
};

Это работает для всего: от игрока до врагов, скайбоксов и триггеров; просто оставьте «компоненты», которые вам не нужны, в данном объекте NULL. Вы хотите поместить все ИИ в список? Тогда просто сделай это во время строительства. С полиморфизмом вы можете использовать все виды различного поведения (например, «ИИ» игрока транслирует ввод контроллера), и кроме этого нет необходимости в базовом базовом классе для всего. Во всяком случае, что это будет делать?

Ваше «обновить все» должно было бы явно вызывать каждый из списков, но это не меняет количество набираемых вами текстов, оно просто перемещает его. Вместо того, чтобы запутанно настраивать наборы, для которых требуются глобальные операции, вы явно перечисляете наборы, для которых нужны операции в момент выполнения операций.

ИМХО, дело не в том, что виртуальные звонки медленные. Дело в том, что «компоненты» игровой сущности не являются однородными. Все они делают совершенно разные вещи, поэтому имеет смысл относиться к ним по-разному. Действительно, между ними нет никакого совпадения, поэтому я снова спрашиваю: какой смысл в базовом классе, если вы не можете использовать указатель на этот базовый класс каким-либо осмысленным образом, не приводя его к чему-то другому?

1 голос
/ 21 апреля 2010

Я использовал аналогичный подход в модульном генераторе синтезированных аудиофайлов.

Кажется, я помню, что заметил, что после программирования 100 различных модулей при кодировании новых модулей на производительность оказало влияние.

В целом я чувствовал, что это хороший подход.

...