Как избежать объекта бога GameManager?


51

Я только что прочитал ответ на вопрос о структурировании игрового кода . Это заставило меня задуматься о вездесущем GameManagerклассе и о том, как часто он становится проблемой в производственной среде. Позвольте мне описать это.

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

Тогда есть зеленый свет, и, чтобы очистить вещи, кто-то пишет GameManager. Вероятно, чтобы держать кучу GameStates, может быть, хранить несколько GameObjects, ничего большого, правда. Милый, маленький, менеджер.

В мирной сфере подготовки производства игра складывается хорошо. У программистов есть достаточное количество ночей сна и множество идей для создания архитектуры с использованием Great Design Patterns.

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

В этот момент, как правило, GameManagerэто действительно большой беспорядок (чтобы оставаться вежливым).

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

Конечно, это классический анти-паттерн: объект бога . Это плохо, боль слияния, боль поддержания, боль понимания, боль преобразования.

Что бы вы предложили, чтобы этого не случилось?

РЕДАКТИРОВАТЬ

Я знаю, что заманчиво обвинять в названии. Конечно, создание GameManagerкласса не такая уж хорошая идея. Но то же самое может случиться с чистым GameApplicationили, GameStateMachineили GameSystemэто будет фаворитом клейкой ленты к концу проекта. Независимо от названия, у вас есть этот класс где-то в вашем игровом коде, это пока только зародыш: вы просто еще не знаете, каким монстром он станет. Так что я не жду ответов «обвиняю в названии». Я хочу, чтобы это не происходило, архитектура кодирования и / или процесс, которым команда может следовать, зная, что это произойдет в какой-то момент производства. Просто очень плохо отбрасывать, скажем, один месяц исправлений и последних функций, просто потому, что все они теперь в одном огромном неуправляемом менеджере.


Будь то GameManager или ControllerOfTheGame, или как вы хотите это называть, я имею в виду избегать класса, который отвечает за управление логикой всей игры. Вы знаете, что делаете это, когда делаете это, независимо от того, что вы планируете назвать. Я думаю, что в моей последней игре у меня был контроллер уровня, который начинался просто для того, чтобы удерживать состояние уровня для сохранения, но если я остановился и подумал об этом, было очевидно, что этот объект должен обрабатывать больше деталей, чем следовало бы.
Брэндон

@brandon Позвольте мне перефразировать мой вопрос для вас: как вы контролируете логику всей игры, не подвергая себя GameManagerвоздействию, которое я описал выше?
Лоран Кувиду

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

Интересно, что XNA создает эту потенциальную катастрофу для вас. По умолчанию класс Game создан для управления всем. Он содержит основные методы обновления, рисования, инициализации и загрузки. Я на самом деле в процессе перехода на новый проект, потому что моей (не такой сложной) игрой стало слишком сложно управлять со всем, втиснутым в этот класс. Кроме того, учебники Microsoft, кажется, поощряют это.
Fibericon

Ответы:


23

Тогда есть зеленый свет, и, чтобы очистить вещи, кто-то пишет GameManager. Возможно, для хранения нескольких GameStates, может быть, для хранения нескольких GameObjects, ничего особенного, правда. Милый, маленький, менеджер.

Знаете, когда я читал это, у меня в голове возникали маленькие тревоги. Объект с именем «GameManager» никогда не будет милым или маленьким. И кто-то сделал это, чтобы очистить код? Как это выглядело раньше? Хорошо, оставим в стороне шутки: имя класса должно быть четким указанием на то, что делает класс, и это должно быть одно (иначе: принцип единой ответственности ).

Кроме того, вы все равно можете получить такой объект, как GameManager, но, очевидно, он существует на очень высоком уровне, и он должен заниматься задачами высокого уровня. Ведение каталога игровых объектов? Может быть. Облегчение общения между игровыми объектами? Конечно. Вычисление столкновений между объектами? Нет! Именно поэтому имя менеджера не одобряется - оно слишком широкое и допускает много злоупотреблений под этим баннером.

Быстрое правило о размерах классов: если вы используете несколько сотен строк кода на класс, что-то начинает идти не так. Не переусердствовав, скажем, что-нибудь свыше 300 LOC - это кодовый запах для меня, и если вы наберете более 1000, должны прозвучать предупреждающие сигналы. Полагая, что каким-то образом 1000 строк кода проще для понимания, чем 4 хорошо структурированных класса по 250 в каждом, вы вводите себя в заблуждение.

Когда время становится проблемой, нет способа написать отдельный класс или разделить этого гигантского менеджера на суб-менеджеров.

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

Что бы вы предложили, чтобы этого не случилось?

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

Хорошей новостью является то, что непосредственные ощутимые преимущества написания чистого кода настолько велики, что почти все разработчики очень быстро осознают свои преимущества. Убедите команду работать так некоторое время, результаты сделают все остальное.

Сложность заключается в том, что развитие чувства плохого кода (и способности быстро придумывать лучший дизайн) является одним из наиболее сложных навыков, которые необходимо освоить при разработке. Мое предложение основано на надежде, что в команде есть кто-то достаточно высокопоставленный, кто сможет это сделать - гораздо проще убедить людей таким образом.

Редактировать - немного больше информации:

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

Тем не менее, специально для разработки игр принятый ответ на этот вопрос в StackOverflow, касающийся советов «особенно игровой архитектуры», гласит:

Следуйте твердым принципам объектно-ориентированного дизайна ....

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

  • Исправьте вещи в тот же день, когда вы их кодируете.
  • Вы будете рады, что сделали это в течение нескольких часов.

Достичь веры в вышесказанное сложно; Мне удалось сделать это для моей собственной работы только потому, что я снова и снова испытывал эффекты. Несмотря на это, мне все еще трудно оправдать исправление кода, когда я мог бы производить больше ... Так что я определенно понимаю, откуда вы пришли. К сожалению, этот совет, возможно, слишком общий, и его нелегко выполнить. Я твердо верю в это, однако! :)

Чтобы ответить на ваш конкретный пример:

Чистый код дяди Боба делает потрясающую работу по обобщению кода хорошего качества. Я согласен почти со всем его содержанием. Поэтому, когда я вспоминаю ваш пример с менеджером класса 30 000 LOC, я не могу действительно согласиться с частью «веских оснований». Я не хочу звучать оскорбительно, но такое мышление приведет к проблеме. Нет веской причины иметь столько кода в одном файле - это почти 1000 страниц текста! Любая выгода от локальности (скорость выполнения или «простота» дизайна) будет немедленно аннулирована разработчиками, которые полностью увязнут в попытках ориентироваться в этом чудовище, и это даже до того, как мы обсудим слияние и т. Д.

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


Это не недостаток, который я видел однажды. Я видел это в нескольких командах, для нескольких проектов. Я видел много талантливых, структурированных людей, борющихся с этим. Когда под давлением 300 строк кода, это не то, о чем заботится ваш производитель. Ни игрок между прочим. Конечно, это мило и мило, но дьявол кроется в деталях. Наихудший случай, GameManagerкоторый я видел до сих пор, составлял около 30 000 строк кода, и я почти уверен, что большая его часть была здесь по очень веской причине. Это, конечно, крайность, но такие вещи случаются в реальном мире. Я прошу способ ограничить этот GameManagerэффект.
Лоран Кувиду

@lorancou - я добавил в пост довольно много дополнительной информации, это было слишком много для комментария, надеюсь, это даст вам немного больше идеи, даже если я не могу указать вам какую-то конкретную архитектурную Примеры.
Даниэль Б.

Ах, я не читал эту книгу, так что это определенно хорошее предложение. О, и я не имел в виду, что есть веская причина для добавления кода в класс из тысячи строк. Я имел в виду, что весь код в таком виде объекта бога делает что-то полезное. Я, вероятно, написал это слишком быстро;)
Лоран Кувиду

@lorancou Хе-хе, это хорошо ... в этом заключается безумие :)
Даниэль Б

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

7

Это не проблема программирования игр, а вообще программирования.

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

Вот почему вы должны хмуриться на любом объектно-ориентированном кодере, который будет предлагать классы с «Менеджером» в названии. Честно говоря, я думаю, что практически невозможно избежать «полугодовых» объектов в игре, но мы просто называем их «системными».

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

Хотя очевидно, что если вы соблазнитесь положить что-то в GameManager, это означает, что у вас нет очень здоровой кодовой базы. Там может быть две проблемы:

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

  • Ваш код плохо структурирован. Вы (ab) используете переменную globals, классы не связаны слабо, нет никакого интерфейса, никакой системы событий и так далее.

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

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


Нет ничего плохого в программировании клейкой ленты, я даю вам это. Но я почти уверен, что в игровых движках есть классы менеджеров, по крайней мере, я их много видел. Они полезны, пока они не становятся слишком большими ... Что не так с a SceneManagerили a NetworkManager? Я не понимаю, почему мы должны их предотвращать или как помогает замена -Manager на -System. Я ищу предложения для замены архитектуры для того, что обычно входит в GameManagerчто-то, менее склонное к раздутию кода.
Лоран Кувиду

Что не так с SceneManager? Ну, а какая разница между Scene и SceneManager? Сеть и сетевой менеджер? С другой стороны: я не думаю, что это проблема архитектуры, но если бы вы могли привести конкретный пример чего-то, что есть в GameManager и не должно быть там, было бы проще предложить решение.
Raveline

Хорошо, так что забудьте о вещи -Manager. Допустим, у вас есть класс, который обрабатывает ваши игровые состояния, давайте его назовем GameStatesCollection. В начале у вас будет только несколько игровых состояний и способов переключения между ними. Затем появляются загрузочные экраны, которые усложняют все это. Затем некоторым программистам приходится иметь дело с ошибкой, когда пользователь извлекает диск, пока вы переключаетесь с Level_A на Level_B, и единственный способ исправить это, не тратя полдня, - это рефакторинг GameStatesCollection. Бум, объект бога.
Лоран Кувиду

5

Итак, привнося одно возможное решение из более корпоративного мира: вы проверяли инверсию управления / внедрения зависимостей?

По сути, вы получаете эту среду, которая является контейнером для всего остального в вашей игре. Он и только он знает, как связать все воедино и каковы отношения между вашими объектами. Ни один из ваших объектов ничего не создает в своих собственных конструкторах. Вместо того, чтобы создавать то, что им нужно, они спрашивают, что им нужно, в рамках IoC; после создания платформа IoC «знает», какой объект необходим для удовлетворения зависимости, и заполняет ее. Слабая муфта FTW.

Когда ваш класс EnemyTreasure запрашивает у контейнера объект GameLevel, фреймворк знает, какой экземпляр ему предоставить. Как это узнать? Может быть, он создал его раньше, может быть, он спрашивает какой-то соседний GameLevelFactory. Дело в том, что EnemyTreasure не знает, откуда появился экземпляр GameLevel. Он только знает, что его зависимость теперь удовлетворена, и он может продолжать жить.

О, я вижу, ваш класс PlayerSprite реализует IUpdateable. Что ж, это здорово, потому что фреймворк автоматически подписывает каждый объект IUpdateable на Timer, где находится основной игровой цикл. Каждый Timer :: tick () автоматически вызывает все методы IUpdatedable :: update (). Легко, правда?

Реально, вам не нужен этот bighugeohmygod фреймворк, чтобы сделать это за вас: вы можете сделать важные кусочки самостоятельно. Дело в том, что если вы создаете объекты типа -Manager, то, скорее всего, вы неправильно распределяете свои обязанности между классами или пропускаете некоторые классы где-то.


Интересно, я впервые услышал об IoC. Это может быть слишком много для программирования игры, но я проверю это!
Лоран Кувиду

IoC, разве мы не использовали это, чтобы назвать это здравым смыслом?
Брайс

Учитывая половину шансов, инженер сделает из всего каркаса. (= Кроме того, я не защищаю использование фреймворка, я
защищаю

3

В прошлом я обычно получал класс бога, если я делаю следующее:

  • сядьте за редактор игры / IDE / блокнот, прежде чем я напишу диаграмму классов (или просто набросок основных объектов)
  • Начните с очень смутного представления об игре и сразу же закодируйте ее, затем начните повторять новые идеи по мере их появления.
  • создайте файл класса с именем GameManager, когда я не создаю игру, основанную на состоянии, как пошаговую стратегию, где GameManager фактически является объектом домена
  • не заботятся об организации кода на ранней стадии

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


1

Я знаю, что этот вопрос уже устарел, но я решил добавить свои 2 цента.

При разработке игр у меня почти всегда есть объект Game. Тем не менее, это очень высокий уровень и сам по себе ничего не «делает».

Например, он сообщает классу Graphics объекту Render, но не имеет ничего общего с процессом рендеринга. Он может попросить LevelManager загрузить новый уровень, но не имеет никакого отношения к процессу загрузки.

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


2
Это совсем не богоподобно - это называется абстракция. :)
Vaughan Hilts
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.