Магия data-driven design

http://goo.gl/33fSWw
  • Перевод
Примечание от переводчика
Из-за отсутствия лаконичного перевода некоторых терминов пришедших к нам из английского языка я предпочту оставить их на языке оригинала. Думаю, что у интересующихся данной отраслью это не вызовет раздражения.


Игры состоят из двух частей: логики и данных. Оживляющих игру при соединении их вместе, но бесполезных по отдельности. Логика определяет основные правила и алгоритмы игрового движка, в то время как данные описывают подробные сведения об игровом содержании и его поведение. Магия в том, что обе эти составляющие отделены друг от друга и могут развиваться отдельно.


Мысль 1: Основы


Прежде всего необходима система, которая может считывать текстовые файлы по запросу (а не только при начальной загрузке программы). Она необходима для следования парадигме data-driven, положенной в основу всего процесса. В каждой игре требуется способ чтения данных из файлов, и несмотря на то, что в конечном итоге игра должна работать с бинарными файлами, во время процесса разработки гораздо удобнее работать с текстовыми. Ведь они просты до невозможного! А также позволяют организовать работу для всей вашей команды эффективнее — не меняя ни одной строки кода, любой член команды (включая тестировщиков и гейм-дизайнеров) может пробовать новое и экспериментировать с различными вариантами существующего. Таким образом, легко реализуемая функция быстро становится незаменимым инструментом в работе.

Мысль 2: Необходимый минимум


Не надо жестко задавать константы в коде. Сложите их все в текстовый файл (или файлы), чтобы их можно было легко изменить без повторной компиляции кода. Например, основной функционал, поведение камеры и т.п. можно полностью описать таким образом. И если будет так, то дизайнер игр, продюсер, мальчишка с соседнего подъезда — все будут в состоянии изменить поведение игры, просто отредактировав нужный файл в блокноте. Да и не все члены команды могут быть близки к программированию и хорошо с ним ладить, поэтому важно помнить об этом, ведь, играясь со значениями постоянных, продюсеры и дизайнеры игр могут добиться нужного им поведения в игре, при этом не тревожа программистов.

Мысль 3: Ничего не ограничивайте


Предположим, что измениться может что угодно (скорее всего так и будет). Если игре требуется режим разделенного экрана, не надо жестко указывать ровно два. Напишите свою игру, которая может поддерживать любое количество экранов со своей логикой для каждой камеры. Количество работы от этого, при правильном подходе к делу, не сильно увеличится. С помощью магии текстовых файлов вы определите сколько экранов должно быть в игре — один, два, четыре или больше. Файлы также зададут начальные данные для камер: положение, направление, угол охвата и наклон. Самое приятное в том, что ваши гейм-дизайнеры тоже обладают прямым доступом к изменению этих параметров.
Игра развивается в полную силу, при гибком проектировании кода. Процесс абстрагирования и вынесения ядра игры сильно поможет в ее проектировании и разработке. Вместо проектирования игры под одну конкретную цель, вы можете проектировать каждый её компонент и его общую функциональность. Эффект от такого подхода будет в том, что вы лучше поймете что вам действительно нужно делать, а не будете слепо следовать описанному поведению в проектной документации к игре.
Например, если игра требует только четыре видов оружия, вы можете запрограммировать совершенную систему, которая описывает все их по отдельности. Однако, если вы абстрагировались от функциональности каждого из видов оружия, используя данные, определяющие поведение, вы в дальнейшем сможете добавить бесчисленное количество видов оружия, чтобы экспериментировать с новыми идеями и динамикой игрового процесса. Такое мышление, в конечном итоге, позволяет игре улучшаться и развиваться.
Вы поверили мне, когда я сказал «Ничего»?
Правда в том, что игры должны быть настраиваемыми, а большие игры должны развиваться от оригинальной версии, следуя сюжету. Ваша игра должна уметь работать с меняющимися правилами, персонажами, расами, оружием, уровнями, схемами управления и объектами. Без этой гибкости любое изменение в игре становится дорогостоящим, и даже незначительное изменение предполагает участие программиста — что по сути просто трата ресурсов. Если же изменения окажутся сложными, то это может привести в итоге к незначительному улучшению оригинального проекта игры, и игра так и не не доживет до раскрытия всех своих возможностей.

Мысль 4: Основной управляющий сценарий


Сценарий — это просто способ задания поведения за пределами программного кода. Сценарии прекрасно подходят для определения последовательности шагов, которые должны произойти в игре, или для игровых событий, которые должны быть вызваны. Например, сценарий для игровой сцены описывающий простую причинно-следственную логику, вроде «что будет при условии завершения квеста» или «какой триггер сработает для данной окружающей среды». Все эти примеры относятся к парадигме data-driven design.
При проектировании сценариев нужно определиться с логикой ветвлений и тем, как она будет организована. Выделают два подхода. Первый — продолжать использовать переменные внутри сценария и сравнивать их операторами сравнения: равенство (=), меньше чем (<) и т.д. Второй подход заключается в непосредственных вызовах оценивающей функции, которая сравнивает переменные, существующие внутри кода, например isLifeBelowPercentage(50). Вы можете использовать и комбинацию этих методов, но при этом постарайтесь сохранить сценарии простыми. Гейм-дизайнеру будет гораздо проще работать с оценивающими функциями, чем с объявленными переменными, их обновлением и сравнением. Также второй подход упростит отладку.
К сожалению, для описания сценариев требуется соответствующий язык. Это означает, что вы должны создать целый синтаксис для определения поведения игры. Язык сценариев включает в себя создание парсера сценариев и, возможно, компилятора для преобразования сценариев в двоичный файл для более быстрого выполнения в дальнейшем. Другой вариант заключается в использовании существующего языка, например Java, но в таком случае может потребоваться большое количество дополнительных компонентов. Чтобы не потратить на язык сценариев много времени, нужно выиграть проектируя системы проще. В целом, наблюдается тенденция придания излишней мощности языку сценариев. Следующая мысль объясняет некоторые подводные камни сложного языка сценариев.

Мысль 5: Когда хорошие сценарии становятся плохими


Использование сценариев для описания поведения в data-driven design — естественное следствие данной методологии. Однако, не нужно забывать про здравый смысл и помнить ключевую идею: отделение логики от данных. Сложная логика отправляется в программный код, а данные остаются снаружи.
Проблема возникает, когда желание основываться на данные заходит слишком далеко. В какой-то момент, вам захочется описать сложную логику внутри сценария. Когда сценарий начинает содержать состояние чего-либо и требует ветвлений, он становится подобен конечному автомату. С увеличением его состояний, невинному писателю сценариев (а также некоторым бедным гейм-дизайнерам) придется работать программистом. Если сценарии становятся слишком сложными, работа возвращается к программисту, который должен описать все сложности на своем строго ограниченном языке. Сценарии должны делать работу людей проще, а не сложнее.
Почему важно хранить сложную логику в коде? Вопрос функциональности и отладки. Поскольку сценарии находятся вне кода, в них дублируются многие понятия, которые существуют в языках программирования. Естественная тенденция давать все больше и больше функциональности сценариям, пока они не станут настоящими языками программирования. Появляются более сложные сценарии, и требуется отслеживать больше информации при отладке, что усложняет изначально простую идею работы со сценариями.
Как вы, наверное, догадались, в сценариях может быть нетривиальная логика, и тогда могут уйти месяцы работы на написание парсера скриптов, компилятора и отладчика. Такое происходило бы, если программисты не понимали, что перед ними уже достаточно хороший компилятор.
Размытая граница
Нет никаких сомнений, что граница между кодом и сценариями размыта. В общем, плохая идея помещать поведение искусственного интеллекта (AI) в сценарии, в то время как хорошей идеей будет размещение в сценариях системных триггеров для придания миру интерактивности. Получается следующее правило: если логика сложная, то она должна быть в коде. Языки сценариев должны оставаться простыми, чтобы не поглотить собой вашу игру (и все ваши программистские ресурсы).
Однако, некоторые игры проектировались с возможностью написания игроками собственных AI. Зачастую, это стрелялки от первого лица, позволяющие добавлять ботов. Когда цель именно такая, подобие языка сценариев настоящим языкам программирования просто неизбежно. В качестве примера можно рассмотреть Quake C. Поскольку создание ботов заложено в саму игру, ресурсы и энергия были потрачены на создание языка сценариев на столько удобного, как и язык C. Язык сценариев такого масштаба довольно трудоемкий и не стоит относиться к нему легкомысленно.
Прежде всего, помните, что вы не хотите, чтобы гейм-дизайнеры и сценаристы занимались программированием игры. Иногда программисты пытаются уйти от ответственности, создавая языки сценариев, чтобы заманить гейм-дизайнеров в программирование игры. В идеальном случае, программисты должны решать серьезные задачи и создавать значительную часть управляющей логики. Иначе за что еще им платят такие большие деньги?!

Мысль 6: Как избежать синдром повторения данных


Распространенная программистская практика — никогда не дублировать код. Если вам нужно некоторое поведение (например, общая функциональность) в двух разных местах, оно должно существовать только в одном месте. Идея применима и к описанию данных с помощью ссылок на глобальные блоки данных. Кроме того, используя ссылки на наборы данных и изменяя некоторые из их значений, вы, в конечном счёте, приближаетесь к концепции наследования.
Наследование — это громадная идея, которую нужно применить к вашим данным. Представьте, что ваша игра населена гоблинами, живущими в подземельях. Ваши данные определяют где стоит каждый гоблин со всеми его свойствами в каждом подземелье. Правильно будет инкапсулировать данные глобального определения в каждый экземпляр гоблина. Для того, чтобы каждый гоблин стал уникальным, ссылка может сопровождаться списком переопределяемых свойств. Такой подход позволит каждому гоблину обладать собственным поведением, при этом не дублируя данные.
Идея может применяться на нескольких уровнях, позволяя каждому набору данных иметь ссылку. Используя эту технику, вы можете сделать глобальное определение гоблина, которое наследуется от базового типа гоблина. Внутри определения для каждого подземелья могут быть указаны обычные или быстрые гоблины. На рисунке продемонстрирована эта концепция наследования с помощью ссылок и переопределения значений.


Мысль 7: Сделайте инструмент для работы с данными


В любой большой игре текстовые файлы в конечном итоге становятся неуправляемыми и с ними становится тяжело работать. Решение этой проблемы — создание инструмента для работы с ними. Назовем этот инструмент редактором игры, редактором уровней, или редактором сценариев, он позволит ускорить разработку игры. Обладание инструментом не меняет методологию data-driven, а лишь делает ее более надежной и эффективной. Время, сохраненное с помощью таких инструментов, оправдывает затраты на их разработку и внедрение.

Заключение


Начать пользоваться методологией data-driven design легко, но довольно трудно вообразить удивительные возможности, появляющиеся при таком подходе.
Игра Total Annihilation — хороший пример такого подхода. Дизайнер Крис Тейлор заложил две расы: Arm и Core. Хоть вся игра и сосредоточена на двух расах, они не были жестко запрограммированы в игру. Теоретически, можно было бы добавить данные для добавления третьей расы, даже после выпуска игры. Несмотря на то, что эту возможность не использовали, Total Annihilation остаётся полностью настраиваемая в этом плане. Так как все юниты определяются данными, новые юниты публиковались еженедельно на сайте игры. На самом деле, многие люди создавали собственных юнитов с функциональностью, которая потрясла самих разработчиков игры.
Data-driven design помог Total Annihilation поддерживать верных фанатов игры в и без того переполненном жанре. Идея, реализованная в Total Annihilation, нашла свое дальнейшее применение и в других играх, например в The Sims, которые также распространяют дополнительный игровой контент через веб сайт. Без приверженности разработчиков к философии data-driven design, такие расширения были бы невозможны.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Нужны ли еще переводы интересных статей из Game Programming Gems на хабре?
  • +20
  • 22,6k
  • 9
Поделиться публикацией
Комментарии 9
    +2
    Интересно, кто вы выбирает нет, ему нужны неинтересные статьи?
      +2
      Возможно, ему нужны оригиналы. Или переводы интересных статей из другого места. Или не на хабре =)
      0
      А ведь можно копнуть еще глубже: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/.
        +7
        Если честно, я не понял, о чём эта статья. Глянул название: «Магия data-driven design»; ага, значит и автор сам не понял, раз это для него магия. Выглядит как набор советов, сводящихся к «не используйте хардкод, выносите данные в конфигурационные файлы». Мысль прекрасная и полезная, но очевидная любому, кто занимался оплачиваемой разработкой больше месяца.

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

        Боюсь, подобные советы и описывающее ими действие приводит к переработкам, космической архитектуре, и срыву всех сроков. Это не подойдёт никому кроме, пожалуй, Valve и Blizzard, но они могут себе это позволить, потому что в начале писали быстро, с костылями и хардкодом.

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

        Наследование: проблемы начинаются с множественного наследования. Что если у вас есть гоблин-стрелок и защитная башня, имеющие общее поведение стрелка. Сделаете базовый класс «ShootingEntity»? А если гоблин ещё и лечит союзников? Лучше определять каждую сущность, как имеющую какое-либо поведение, в этом примере гоблин обладает MovingBehaviour, ShootingBehaviour, UnitBehaviour, HealingBehaviour; башня только ShootingBehaviiour и BuildingBehaviour. В общем, структура Entity-Behaviour в целом куда гибче, чем обычное наследование.
          0
          В свободное время пишу sandbox, тоже пришел к выводу что система Entity-Behaviour наиболее гибкая из всего, до чего я додумывался.
            0
            «… выносите данные в конфигурационные файлы». Мысль прекрасная и полезная, но очевидная любому, кто занимался оплачиваемой разработкой больше месяца.


            А если бы он позанимался оной разработкой ещё хотя бы месяц, мысль перестала бы казаться такой уж очевидной. Вынося данные в конфиг, программист теряет над ними контроль, и теряет статическую проверку компилятором. Если данные записаны в синтаксисе языка, то они заведомо не будут содержать неправильный разделитель целой и дробной части, «минус −» вместо «программистского минуса -», киррилическую букву «с» вместо латинской «c» в каком-нибудь идентификаторе-enum'е. Целый ряд ошибок, которые раньше ловились при компиляции, теперь могут всплыть в любое время в рантайме и повлечь гневные багрепорты от активных пользователей, разнёсших конфигурацию в хлам своими правками.

            Конфигурирование в коде — strongly typed.
            Конфигурирование в текстовых файлах — stringly typed.

            image
              +1
              Я не имел в виду правки конечных пользователей. В статье и моём комментарии говорится прежде всего о правках в конфигурационных файлах, которыми занимаются профессионалы не-программисты. Если конечный пользователь меняет данные, то ответственность лежит на нём. Делаем кнопу «Reset to default», спим спокойно.

              Если ошибается геймдизайнер, то это тоже поправимо: единый парсер для всех файлов, который обучен разным разделителям или генерирует исключение для каждого подозрительного символа. Опять же, потеря контроля над сырыми данными это копеечная цена за экономию времени программистов и создание удобного инструмента для ГД.
            0
            Для мобильной разработки под iOS не очень подходит. Нет внешних текстовых файлов, легко редактируемых пользователем.
              0
              Статья предназначенна для молодых разработчков. Все описано примитивно но понятно. У нас использовался xml, никаких сценариев не создавалось, создавать второй язык программирования плохая затея. Вместо этого, система проектировалась так, что все моменты можно было указать декларативно, соотвествено данные не были похожи на сценарии. При добавлении сложного функционала, он приобретал вид экшена или валидатора и добавлялся в фабрику, после чего его можно было использовать в декларативном описании чего либо.

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

              Самое читаемое