При создании игр вы часто создаете следующий игровой объект, от которого наследуются все сущности:
public class GameObject{
abstract void Update(...);
abstract void Draw(...);
}
Таким образом, в цикле обновления вы перебираете все игровые объекты и даете им возможность изменить состояние, а затем в следующем цикле прорисовки вы повторяете все игровые объекты и даете им возможность рисовать себя.
Хотя это довольно хорошо работает в простой игре с простым рендерингом вперед, это часто приводит к нескольким гигантским игровым объектам, которым нужно хранить свои модели, множественные текстуры и, что хуже всего, метод толстого рисования, который создает тесную связь между игровым объектом, текущая стратегия рендеринга и любые классы рендеринга.
Если бы я изменил стратегию рендеринга с прямой на отложенную, мне пришлось бы обновить множество игровых объектов. И игровые объекты, которые я делаю, не так многоразовы, как могли бы быть. Конечно, наследование и / или композиция могут помочь мне бороться с дублированием кода и немного упростить изменение реализации, но все еще ощущается недостаток.
Возможно, лучшим способом было бы полностью удалить метод Draw из класса GameObject и создать класс Renderer. GameObject по-прежнему должен содержать некоторые данные о его визуальных элементах, например, какую модель он должен представлять и какие текстуры должны быть нарисованы на модели, но как это сделать, будет оставлено для визуализации. Однако при рендеринге часто встречается много граничных случаев, поэтому, хотя это устранит тесную связь между GameObject и Renderer, Renderer все равно должен был бы знать все игровые объекты, которые делали бы его жирным, все знали и тесно связаны. Это нарушило бы немало хороших практик. Возможно, Data-Oriented-Design справится с этой задачей. Игровые объекты, безусловно, будут данными, но как будет управлять этим средством визуализации? Я не уверен.
Так что я в растерянности и не могу придумать хорошего решения. Я пытался использовать принципы MVC, и в прошлом у меня были некоторые идеи о том, как использовать это в играх, но в последнее время это не кажется таким применимым, как я думал. Я хотел бы знать, как вы все решаете эту проблему.
В любом случае, давайте повторим, мне интересно, как можно достичь следующих целей дизайна.
- Нет логики рендеринга в игровом объекте
- Слабая связь между игровыми объектами и движком рендеринга
- Не все знающие рендерер
- Предпочтительно переключение во время выполнения между движками рендеринга
Идеальная настройка проекта - это отдельная «игровая логика» и проект логики рендеринга, которые не должны ссылаться друг на друга.
Этот мысленный ход начался, когда я услышал, как Джон Кармак сказал в твиттере, что у него настолько гибкая система, что он может менять движки рендеринга во время выполнения и даже может сказать своей системе использовать оба рендерера (программный рендер и аппаратно-ускоренный рендерер) в то же время, чтобы он мог проверять различия. Системы, которые я запрограммировал до сих пор, даже не настолько гибки
источник
Что я сделал для своего собственного движка - это сгруппировал все в модули. Итак, у меня есть свой
GameObject
класс, и он содержит ручку для:Итак, у меня есть
Player
класс иBullet
класс. Оба являются производными отGameObject
и добавляются вScene
. НоPlayer
имеет следующие модули:И
Bullet
имеет эти модули:Этот способ организации вещей избегает «Алмаза Смерти», где у вас есть a
Vehicle
, aVehicleLand
и a,VehicleWater
а теперь вы хотите aVehicleAmphibious
. Вместо этого у вас естьVehicle
и он может иметьModuleWater
иModuleLand
.Дополнительный бонус: вы можете создавать объекты, используя набор свойств. Все, что вам нужно знать, это базовый тип (Player, Enemy, Bullet и т. Д.), А затем создавать дескрипторы для модулей, которые вам нужны для этого типа.
В моей сцене я делаю следующее:
Update
для всехGameObject
ручек.ModuleCollision
ручка.UpdatePost
для всехGameObject
ручек, чтобы сообщить об их окончательном положении после физики.m_ObjectsCreated
списка вm_Objects
список.И я мог бы организовать это дальше: по модулям, а не по объектам. Затем я бы отображал список
ModuleSprite
, обновлял кучуModuleScriptingBase
и делал коллизии со спискомModuleCollision
.источник
GameObject
(например, способ визуализации «змеи» спрайтов), вам нужно либо создать дочерний элементModuleSprite
для этой конкретной функции (ModuleSpriteSnake
), либо добавить новый модуль в целом (ModuleSnake
). К счастью, они всего лишь указатели, но я видел код, гдеGameObject
буквально все, что мог сделать объект.