Например, наличие базового класса GameObject с глубокой иерархией наследования может быть полезным для обслуживания ...
На самом деле, глубокие иерархии, как правило, хуже для удобства обслуживания, чем мелкие, а современный архитектурный стиль для игровых объектов имеет тенденцию к поверхностным, основанным на агрегации подходам .
Однако я думаю, что такой подход может создать проблемы с производительностью. С другой стороны, данные и функции всех игровых объектов могут быть глобальными. Это было бы головной болью при обслуживании, но могло бы быть ближе к оптимально выполненному игровому циклу.
Цикл, который вы показали, потенциально имеет проблемы с производительностью, но это не так, как подразумевает ваше последующее утверждение, потому что у вас есть данные экземпляра и функции-члены в GameObject
классе. Скорее проблема в том, что если вы относитесь к каждому объекту в игре как к одному и тому же, вы, вероятно, не группируете эти объекты разумно - они, вероятно, случайно разбросаны по всему списку. Следовательно, каждый вызов метода обновления для этого объекта (независимо от того, является ли этот метод глобальной функцией или нет, и содержит ли объект данные экземпляра или «глобальные данные», плавающие в какой-то таблице, в которую вы индексируете или что-то подобное), отличается от вызова обновления в последней итерации цикла.
Это может увеличить нагрузку на систему, поскольку вам может понадобиться перенести в память соответствующую функцию в память и из нее и чаще заполнять кэш команд, что приведет к более медленному циклу. То, что это можно наблюдать невооруженным глазом (или даже в профилировщике), зависит от того, что именно считается «игровым объектом», сколько из них существует в среднем и что еще происходит в вашем приложении.
Компонентно-ориентированные объектные системы в настоящее время являются популярной тенденцией, используя философию, согласно которой агрегация предпочтительнее наследования . Такие системы потенциально позволяют разделить логику «обновления» компонентов (где «компонент» грубо определяется как некоторая единица функциональности, например, вещь, представляющая физически смоделированную часть некоторого объекта, которая обрабатывается физической системой). ) к нескольким потокам - различаются по типу компонента - если это возможно и желательно, что может привести к увеличению производительности. По крайней мере, вы можете организовать компоненты так, чтобы все компоненты данного типа обновлялись вместе , оптимально используя кэш процессора. Пример такой компонентно-ориентированной системы обсуждается в этой теме .
Такие системы часто сильно разобщены, что также является благом для обслуживания.
Ориентированное на данные проектирование - это связанный подход - он ориентируется на данные, требуемые от объектов, в качестве первоочередной задачи, чтобы эти данные могли эффективно обрабатываться в больших объемах (например). Как правило, это означает организацию, которая пытается объединить данные, используемые для одного и того же целевого кластера, и использовать их одновременно. Это не является принципиально несовместимым с дизайном ОО, и вы можете найти некоторые разговоры по этому вопросу здесь, в GDSE в этом вопросе .
По сути, более оптимальный подход к игровому циклу был бы вместо вашего оригинального
foreach(GameObject g in gameObjects) g.update();
что-то вроде
ProcessUserInput();
UpdatePhysicsForAllObjects();
UpdateScriptsForAllObjects();
UpdateRenderDataForAllObjects();
RenderEverything();
В таком мире, каждый GameObject
может иметь указатель или ссылку на свой собственный PhysicsData
или Script
или RenderData
, за исключением случаев , когда вам может понадобиться , чтобы взаимодействовать с объектами на индивидуальной основе, но фактическая PhysicsData
, Scripts
, RenderData
и так далее были бы все собственностью их соответствующих подсистем (физический симулятор, среда размещения скриптов, рендерер) и обрабатываются навалом, как указано выше.
Это очень важно отметить , что этот подход не является чудодейственным и не всегда дает прирост производительности (хотя это , как правило , лучше дизайн , чем глубокое дерево наследования). Вы особенно вероятно заметите, по существу, никакой разницы в производительности, если у вас очень мало объектов или очень много объектов, для которых вы не можете эффективно распараллелить обновления.
К сожалению, нет такого волшебного цикла, который является наиболее оптимальным - каждая игра отличается и может потребовать настройки производительности по-разному. Поэтому очень важно измерить (профилировать) вещи, прежде чем слепо следовать совету какого-то случайного парня в Интернете.