Як уникнути об'єкта бога GameManager?


51

Я просто прочитав відповідь на запитання про структурування ігрового коду . Це змусило мене замислитися про всюдисущий GameManagerклас, і як це часто стає проблемою у виробничому середовищі. Дозвольте мені описати це.

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

Потім з’являється зелене світло, і намагаючись прибрати речі, хтось пише а GameManager. Напевно, щоб утримати купу GameStates, можливо, зберігати кілька GameObjects, нічого великого, насправді. Милий, маленький, менеджер.

У мирній царині попереднього виробництва гра непогано формується. Кодери мають належний сон і багато ідей для архітектури речі з великими моделями дизайну.

Тоді виробництво починається і незабаром, звичайно, настає час крихти. Збалансоване харчування давно минуло, помилка відслідковує проблеми, люди піддаються стресу, і гру потрібно випустити вчора.

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

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

Звичайно, це класичний анти-візерунок: об'єкт бога . Це погано, біль злитися, біль підтримувати, біль зрозуміти, біль перетворити.

Що б ви запропонували, щоб цього не сталося?

EDIT

Я знаю, що звинувачувати в найменуванні заманливо. Звичайно, створення GameManagerкласу не така чудова ідея. Але те ж саме може статися з чистою GameApplicationабо GameStateMachineабо GameSystemщо закінчується час клейкої стрічки улюбленої до кінця проекту. Незалежно від найменування, у вас є клас десь у вашому ігровому коді, це поки що ембріон: ви просто ще не знаєте, яким монстром він стане. Тож я не очікую відповідей на "звинувачення у називанні". Я хочу, щоб не допустити цього, архівування кодування та / або процес, який може прослідкувати команда, знаючи, що це станеться в якийсь момент виробництва. Це просто погано, щоб викинути, скажімо, один місяць виправлення та функції в останню хвилину, просто тому, що всі вони зараз в одному величезному незбагненному менеджері.


Незалежно від того, називається він GameManager або ControllerOfTheGame, або як ви хочете його назвати, я маю на увазі уникання класу, який відповідає за логіку всієї гри. Ви знаєте, що це робите, коли ви це робите, незалежно від того, що ви плануєте називати. Я думаю, що в моїй останній грі у мене був контролер рівня, який запускався просто для того, щоб утримувати стан рівня для економії, але якби я зупинився і задумався про це, було очевидно, що цей об'єкт мав обробляти більше деталей, ніж повинен.
Брендон

@brandon Дозвольте перефразувати моє запитання до вас: як ви керуєте логікою всієї гри, не піддаючи себе ефекту-ефекту, GameManagerякий я описав вище?
Лоран Кувіду

Залежить від логіки. Очевидні речі, такі як гравець, ворог, структура тощо, що, очевидно, має власну логіку, ставлять у свої класи. Те, про що ви, здається, найбільше запитуєте, це обробляти стан гри. Зазвичай буде клас, який слідкує за станом всієї гри, але, на мій досвід, це одне з останніх речей, про яке потрібно потурбуватися при прототипуванні, і його мета повинна бути дуже чіткою. В основному ви або робите початкове планування перед складанням прототипів, або починаєте з нуля, коли ви починаєте виробництво, щоб уникнути використання класів сміття, які були лише для тестування.
Брендон

Цікаво, що XNA створює для вас цю потенційну катастрофу. За замовчуванням створюється Ігровий клас, який керує, ну, усім. Він містить основні методи оновлення, малювання, ініціалізації та завантаження. Я фактично переходжу до нового проекту, тому що моя (не така складна) гра стала надто складною, щоб керувати всім, набитим у цьому класі. Крім того, навчальні матеріали Microsoft, схоже, заохочують це.
Fibericon

Відповіді:


23

Потім з'являється зелене світло, і намагаючись прибрати речі, хтось пише GameManager. Ймовірно, щоб влаштувати купу GameStates, можливо, щоб зберігати кілька GameObjects, нічого великого, насправді. Милий, маленький, менеджер.

Знаєте, коли я це читав, у мене в голові мало тривог. Об'єкт з назвою "GameManager" ніколи не буде симпатичним чи маленьким. А хтось це зробив, щоб очистити код? Як це виглядало раніше? Гаразд, жартуємо вбік: назва класу має бути чіткою ознакою того, що робить клас, і це повинно бути одне (також: принцип єдиної відповідальності ).

Крім того, ви можете все-таки опинитися з таким об'єктом, як GameManager, але, очевидно, він існує на дуже високому рівні, і він повинен стосуватися завдань високого рівня. Ведення каталогу ігрових об’єктів? Можливо, Полегшення спілкування між ігровими об’єктами? Звичайно. Обчислення зіткнень між об'єктами? Немає! Ось чому назви менеджера імен нахмурюються - воно занадто широке і дозволяє сильно зловживати під цим банером.

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

Коли час стає проблемою, немає можливості написати окремий клас або розділити цього гігантського менеджера на субменеджерів.

Я думаю, що це так лише тому, що проблемі дозволено поширюватися до того моменту, коли все є повним безладом. Практика рефакторингу - це дійсно те, що ви шукаєте - вам потрібно постійно вдосконалювати дизайн коду невеликими кроками .

Що б ви запропонували, щоб цього не сталося?

Проблема не технологічна, тому не слід шукати технологічних виправлень. Проблема полягає в тому, що у вашій команді є тенденція до створення монолітних фрагментів коду, і віра в те, що в середньо / довгостроковій перспективі так чи інакше вигідно працювати так. Також здається, що команді бракує сильної архітектурної позиції, яка керувала б архітектурою гри (або, принаймні, ця людина занадто зайнята для виконання цього завдання). В основному єдиним виходом є те, щоб члени команди визнали, що таке мислення неправильне. Це нікому не сприяє. Якість продукту погіршиться, а команда лише витратить ще більше ночей на виправлення речей.

Хороша новина полягає в тому, що негайні, відчутні переваги написання чистого коду настільки великі, що майже всі розробники дуже швидко усвідомлюють свої переваги. Переконайте команду працювати таким чином деякий час, результати зроблять решту.

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

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

Взагалі, я не думаю, що ваша проблема обмежується розробкою ігор. По суті, це проблема інженерії програмного забезпечення, звідси мої коментарі в цьому напрямку. Що може відрізнятись від природи індустрії розвитку ігор, чи більше її орієнтовані результати та терміни, ніж інші типи розвитку, я не впевнений.

Зокрема, для розробки ігор, відповідь на це питання в StackOverflow щодо порад щодо "особливо архітектури ігор" говорить:

Дотримуйтесь твердих принципів об'єктно-орієнтованого дизайну ....

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

  • Виправте речі в той же день, коли ви їх кодуєте.
  • Ви будете раді, що зробили це протягом кількох годин.

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

Щоб відповісти на ваш конкретний приклад:

Чистий код дядька Боба робить дивовижну роботу підсумовувати, що таке хороша якість. Я випадково погоджуюся з майже всім його змістом. Отже, коли я думаю про ваш приклад класу 30 000 LOC-менеджера, я не можу погодитися з частиною "доброї причини". Я не хочу звучати образливо, але думаючи, що таким чином призведе до проблеми. Немає вагомих причин мати стільки коду в одному файлі - це майже 1000 сторінок тексту! Будь-яка перевага локальності (швидкість виконання або простота дизайну) буде негайно скасована тим, що розробники повністю заблукають, намагаючись орієнтуватися на цю жахливість, і це ще до того, як ми обговоримо об'єднання тощо.

Якщо ви не впевнені, найкращим моїм пропозицією було б взяти копію вищезгаданої книги та переглянути її. Застосування такого типу мислення призводить до того, що люди добровільно створюють чистий, зрозумілий код, який добре структурований.


Це не вада, яку я бачив один раз. Я бачив це в декількох командах, у кількох проектах. Я бачив багато талановитих, структурованих людей, які борються з цим. Коли тиск перебуває під тиском 300 рядків коду - це не те, про що виробник піклується. Ні гравець btw. Звичайно, це приємно і мило, але чорт у деталях. Найгірший випадок, який GameManagerя бачив до цього часу, складав близько 30 000 рядків коду, і я впевнений, що більшість із них були тут з дуже доброї причини. Це, звичайно, екстремально, але те, що відбувається в реальному світі. Я прошу способу обмежити цей GameManagerефект.
Лоран Кувіду

@lorancou - Я додав зовсім небагато додаткової інформації до публікації, це було занадто багато для коментаря, сподіваємось, це дає вам трохи більше ідеї, навіть якщо я не можу вказати вам на якусь конкретну архітектуру приклади.
Даніель Б

Ах, я не читав цю книгу, тому це, безумовно, гарна пропозиція. О, і я не мав на увазі, що є достатній привід додати код до класу в тисячі рядків. Я мав на увазі, що весь код у такому об'єкті бога робить щось корисне. Я, мабуть, написав це занадто швидко;)
Лоран Кувіду

@lorancou Hehe, це добре ... таким чином лежить божевілля :)
Даніель B

"Практика рефакторингу - це дійсно те, що ви шукаєте. Вам потрібно постійно вдосконалювати дизайн коду невеликими кроками." Думаю, у вас там дуже сильний момент. Мес приваблює безлад, ось справжня причина цього, тому з безладом потрібно боротися довго. Я прийму цю відповідь, але це не означає, що немає значення в інших!
Лоран Кувіду

7

Це насправді не проблема ігрового програмування, а взагалі програмування.

весь вихідний код насправді тут для управління грою.

Ось чому вам слід нахмуритися на будь-якому об'єктно-орієнтованому кодері, який запропонує класи в імені "Менеджер". Справедливо кажучи, я думаю, що практично неможливо уникнути "напівродових" об'єктів у грі, але ми їх просто називаємо "системними".

Будь-яке програмне забезпечення, як правило, забруднюється, коли наблизиться термін. Це частина роботи. І з " програмуванням типу воздуховода " немає нічого по суті , особливо, коли ви повинні доставити свій товар за кілька годин. Ви не можете повністю позбутися цієї проблеми, ви можете просто її зменшити.

Хоча, очевидно, якщо ви спокусилися помістити речі в GameManager, це означає, що у вас немає дуже здорової бази коду. Можуть виникнути дві проблеми:

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

  • Ваш код недостатньо структурований. Ви (аб) використовуєте змінну глобальних даних, класи не зв'язані між собою, інтерфейсу немає, системи подій тощо.

Якщо код здається нормальним, вам доведеться пам’ятати для кожного проекту «що пішло не так» і уникати цього наступного разу.

Нарешті, це також може бути питання управління командою: чи було достатньо часу? Чи знали всі про архітектуру, за якою ви збиралися? Хто, до біса, допустив вчинення класу зі словом "Менеджер" у ньому?


З програмуванням клейкої стрічки немає нічого поганого, я вам це дарую. Але я впевнений, що в ігрових двигунах все навколишнє, і принаймні я їх багато бачив. Вони корисні до тих пір, поки вони не зростуть занадто великими ... Що не так з a SceneManagerчи a NetworkManager? Я не розумію, чому ми повинні їх запобігати, або як допомагає замінити -Manager на -System. Я шукаю пропозиції щодо архітектури заміни того, що зазвичай перетворюється на GameManagerщось менш схильне до коду.
Лоран Кувіду

Що не так з SceneManager? Ну, в чому різниця між Scene та SceneManager? Мережа та мережевий менеджер? З іншого боку: я не думаю, що це питання архітектури, але якщо ви можете навести конкретний приклад того, що є в GameManager і не повинно бути там, було б простіше запропонувати рішення.
Raveline

Гаразд, так що забудьте про річ -Manager. Скажімо, у вас клас, який обробляє ваші стани гри, давайте назвати його GameStatesCollection. На початку у вас буде лише кілька станів гри та способи перемикання між ними. Потім є екрани для завантаження, які все це ускладнюють. Тоді деякому програмісту доводиться мати справу з помилкою, коли користувач викидає диск під час переходу з Level_A на Level_B, і єдиний спосіб виправити його, не витрачаючи на рефакторинг півдня GameStatesCollection. Бум, об’єкт бога.
Лоран Кувіду

5

Таким чином, приведення одного можливого рішення з більш Enterprise-y світу: чи перевірили ви інверсію введення контролю / залежності?

В основному, ви отримуєте цей фреймворк, який є контейнером для всього іншого у вашій грі. Він, і лише це, знає, як зв'язати все разом і які стосунки між вашими об’єктами. Жоден із ваших об'єктів нічого не створює у власних конструкторах. Замість того, щоб створювати те, що їм потрібно, вони запитують те, що їм потрібно в рамках IoC; при створенні ІоК-рамки «знає», який об’єкт необхідний для задоволення залежності і заповнює її. Вільна муфта FTW.

Коли ваш клас EnemyTreasure запитує контейнер для об'єкта GameLevel, фреймворк знає, який екземпляр йому надати. Звідки це знати? Можливо, він це ініціював раніше, можливо, він запитує десь поблизу GameLevelFactory. Справа в тому, що EnemyTreasure не знає, звідки взявся екземпляр GameLevel. Він знає лише, що її залежність тепер задоволена і вона може продовжувати своє життя.

О, я бачу, ваш клас PlayerSprite реалізує IUpdateable. Ну це чудово, адже фреймворк автоматично підписує кожен IUpdateable об’єкт на Таймер, саме там знаходиться основний цикл гри. Кожен таймер :: галочка () автоматично викликає всі методи IUpdateable :: update (). Легко, правда?

Реально вам не потрібна ця рамка біггеогмігод, щоб зробити це за вас: ви можете зробити важливі шматочки цього самостійно. Справа в тому, що якщо ви виявите, що ви робите об'єкти типу -Manager, швидше за все, ви не розподіляєте свої обов'язки між своїми класами правильно або десь не вистачаєте.


Цікаво, що вперше я чув про IoC. Це може бути надто великодумним для програмування ігор, але я перевірю це!
Лоран Кувіду

IoC, Хіба ми просто не називали це здоровим глуздом?
брич

Давши половину шансів, інженер зробить рамку з нічого. (= Крім того, я не виступаю за використання рамок, я виступаю за архітектурний зразок.
drhayes

3

У минулому я, як правило, закінчував клас богів, якщо роблю наступне:

  • сідайте за редактор ігор / IDE / блокнот, перш ніж я виписав діаграму класів (або просто ескіз основних об'єктів)
  • Почніть з дуже розпливкої ідеї гри та негайно її кодуйте, а потім почніть повторювати нові ідеї по мірі їх надходження
  • створити файл класу під назвою GameManager, коли я не створюю гру, засновану на державі, як покрокову стратегію, де GameManager насправді є об'єктом домену
  • не хвилюйтеся щодо організації коду на початку

Це може бути суперечливим, що я сміюсь, якщо це так, але я не можу подумати жодного разу навіть при прототипуванні того, що вам не слід писати дуже базову діаграму класів, перш ніж писати відтворюваний прототип. Я, як правило, тестую технічні ідеї перед плануванням, але це з цього приводу. Отже, якщо я хотів зробити портал, я б написав дуже базовий код, щоб портали працювали, тоді сказати нормально, прекрасний час для незначного планування, тоді я можу працювати над прототипом, який можна відтворити.


1

Я знаю, що це питання вже давнє, але я подумав, що додаю свої 2 копійки.

При розробці ігор я майже завжди маю об’єкт Ігри. Однак це дуже високий рівень, і він насправді не «робить нічого» сам.

Наприклад, він повідомляє клас "Графіка" на "Візуалізація", але не має нічого спільного з процесом візуалізації. Він може попросити LevelManager завантажити новий рівень, але не має нічого спільного з процесом завантаження.

Коротше кажучи, я використовую напів богоподібний об'єкт, щоб оркеструвати використання інших класів.


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