Технический геймдизайн. Конфиги, баланс и контент на примере PC стратегии

Константин Сахнов, геймдизайнер

Одна из распространённых задач, с которой сталкиваются как начинающие, так и опытные геймдизайнеры — описание большого объёма контента для передачи его параметров в движок. Задача не из лёгких, учитывая что найти материалы о технических аспектах геймдизайна очень тяжело, особенно на русском языке. Что ж, давайте разбираться, как перенести данные в движок.

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

Сформулируем задачу: допустим, мы работаем над продуктом, в котором много типового контента. Наша задача — записать весь контент в формате, который примет движок игры.

Я рассмотрю весь процесс на примере игры над которой работаю, чтобы было наглядней. Это стратегия, симулятор терраформирования планет с последовательным набором миссий аля StarCraft и упором на сюжет и нарративные механики. Игрок — представитель цивилизации древних, превращающий безжизненные планеты в рай и заселяющий их эльфами, орками и другими разумными существами.

Ключевые механики:  

  • Строительство зданий на планете и её терраформирование;

  • Развитие древа талантов генератора пустоты.

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

1. Утверждение формата данных

Первая задача — договориться с программистами, в каком формате будем передавать в движок информацию о контенте.

Для её решения существует множество вариантов. Если бы контент, который геймдизайнер хочет видеть в игре, был в единичном экземпляре, мы могли бы воспользоваться механизмом ScriptableObject или прописать данные в скриптах непосредственно в Unity / UE. Это удобно, когда точно известно, что в игре будет ровно 3 постройки, или мы имеем дело с уникальными объектами на сцене.

В нашем же случае построек может быть 30 или 100. У каждой из них есть множество уровней развития, каждый из которых накладывает на планету, зону или постройку определённый эффект или даёт производство ресурсов. А главное, мы не можем гарантировать, что в будущем мы не захотим добавить ещё пару десятков строений или удалить какие-то из имеющихся.

Для решения данной задачи удобно использовать скриптовые языки разметки данных JSON или XML. Реже встречается реализация конфигов на стареньком Lua или более совершенном YAML.

Рис 1. Фрагмент страницы Confluence со списком параметров JSON-скрипта, описывающего сценарии

Рис 1. Фрагмент страницы Confluence со списком параметров JSON-скрипта, описывающего сценарии

Задача геймдизайнера в данном случае — сформировать полный список параметров каждого типа объектов с указанием типа данных и возможных лимитов.

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

Рис. 2. Скриншот описания структуры данных JSON-конфига стоимости улучшения построек в Unity

Рис. 2. Скриншот описания структуры данных JSON-конфига стоимости улучшения построек в Unity

2. Связи и структура данных

Никакие объекты в больших играх обычно не являются изолированными — они как-то взаимосвязаны с данными другими. Чтобы проще и нагляднее увидеть все эти связи и сформировать структуру данных игры, удобно использовать терминологию баз данных (например, SQL) и рассматривать список тех же построек как отдельную таблицу. Тогда важно вспомнить такие понятия как:

  • Внешние ключи — идентификаторы (обычно в виде натуральных чисел), по которым объекты из одних таблиц могут получать информацию из других;

  • Типы связей — логика использования внутренних и внешних ключей. Обычно выделяют:

    • Один к одному: идентификатор объекта, к примеру, постройки, в одной таблице полностью соответствует такому же идентификатору в другой;

    • Один ко многим: одной постройке соответствует много уровней апгрейда, на каждом из которых она может иметь разные свойства;

    • Многие ко многим: разные уровни постройки могут применять разные эффекты. К примеру, здание «Четырёхмерный завод» на 5-м уровне открывает дополнительный слот под строительство в зоне, где он расположен. И «Небесные филатуры» также накладывают эффект, добавляющий в зону новый слот для строительства. Прим.: если есть техническая возможность, старайтесь создавать эффекты и другие поля с уникальными id, отдавая предпочтение связи один ко многим.

  • Нормальная форма БД — это набор правил, предполагающий недопущение избыточности данных. Их существует несколько, информации об этом довольно много в сети.

Формирование структуры данных конфигурационных файлов проекта проще показать на примере. Давайте кратко рассмотрим механику строительства зданий.

Планета игрока разделена на зоны, каждая из которых состоит из гексов. В одном гексе может располагаться одна постройка. Помимо строений на карте могут быть источники и аномалии. Источник — это заготовка для здания. К примеру, месторождение кварца позволит построить в данной клетке только одну конкретную постройку, которая будет добывать камень. Аномалия же — это виртуальный контейнер, содержащий в себе эффекты, которые начинают работать после её исследования, и/или ресурсы, которые можно забрать, исследовав аномалию.

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

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

Рис. 3. Фрагмент страницы Confluence с описанием развития зданий

Рис. 3. Фрагмент страницы Confluence с описанием развития зданий

Как видно на скриншоте, здание «Планетарный потрошитель» (да, я фанат Dead Space!) требует металл для строительства. Будучи построенным, оно начинает производить 1 камень в ход. У игрока есть возможность дважды улучшить его. При улучшении до уровня 2 игрок выбирает один из двух вариантов: увеличить добычу камня ещё на 1 или сразу на 2. Второй вариант обойдётся игроку дороже на 100% + 30% (коэффициент инфляции апгрейдов). При улучшении здания до 3-го уровня постройка не только производит камень, но и получает особый эффект на выбор: получить дополнительно 1 металла или камня в ход с 50% вероятностью.

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

Рис. 4. Взаимосвязи между списками контента (таблицами) в конфигах построек

Рис. 4. Взаимосвязи между списками контента (таблицами) в конфигах построек

Главная таблица текущей модели — это Buildings. В ней описаны все постройки, доступные в игре. Каждой из них присвоен уникальный численный номер — id (натуральное число). По этому номеру мы будем идентифицировать постройку во всех таблицах. Допустим, нам нужно получить текстовое описание или ассет (например 3D-модельку) конкретного здания. В этом случае программный код пробегается по таблице Buildings и находит в ней вхождение с заданным уникальным id. Далее он забирает нужную информацию из этого вхождения.

Рис. 5. Скриншот Google Таблиц с фрагментом контента таблицы Buildings

Рис. 5. Скриншот Google Таблиц с фрагментом контента таблицы Buildings

6Помимо таблицы Buildings, есть таблица Building_Levels. В ней прописан список всех уровней всех зданий. В частности, для здания «Планетарный потрошитель» будет 5 вхождений: одно для 1-го уровня постройки и по 2 для 2-го и 3-го уровня. У всех этих вхождений будет разный id, но одинаковый building_id. Он является внешним ключём, по которому можно однозначно найти постройку из списка Buildings. Такая структура данных позволяет продукту иметь разное число уровней с разными бонусами для каждой постройки. Геймдизайнеры смогут спроектировать постройки источники, которые будут просто добывать ресурсы и улучшаться по 10 раз. Или сложные космические порты, которые можно улучшить всего пару раз, но 5-ю разными способами. Это демонстрация работы связи один ко многим.

Рис. 6. Скриншот Google Таблиц с фрагментом контента таблицы Building_Levels

Рис. 6. Скриншот Google Таблиц с фрагментом контента таблицы Building_Levels

Как мы уже говорили, улучшение здания до нового уровня и выбор одно из вариантов таких улучшений могут иметь разную стоимость. Логично, что расширение производства на +3 должно стоить дороже, чем расширение на +1. Иначе мы допустим ошибку, называемую дисбалансом «бесполезности или гиперполезности» по Гарфилду: зачем покупать за дорого то, что хуже, чем более дешёвый аналог. Чтобы спроектировать разную стоимость улучшения до каждого из вариантов уровней, удобно ввести дополнительную таблицу Building_Upgrade_Costs. Её структура похожа на таблицу уровней, только внешним ключом, на который ссылаются вхождения в эту таблицу, теперь являются не здания, а уже конкретные уровни. Для каждого такого уровня прописан список ресурсов, который необходимо отдать для апгрейда.

Рис. 7. Скриншот Google Таблиц с фрагментом контента таблицы Building_Upgrade_Costs

Рис. 7. Скриншот Google Таблиц с фрагментом контента таблицы Building_Upgrade_Costs

Аналогично описываются таблицы Building_Effects и Building_Production, отвечающие за особые эффекты типа «С вероятностью 50% добывает 1 металл или 1 камень» и за количество производимых ресурсов соответственно. У внимательного читателя мог возникнуть вопрос, что за таблицы Cards и Cards_rares отмечены в структуре данных механики строительства. Дело в том, что в нашей игре игрок строит здания, разыгрывая карты перемещением их в нужный ему гекс.

3. Пайплайн генерации контента

Напомню, мы рассматриваем процесс работы с конфигурационными файлами и описанием контента на примере PC стратегии, разработкой которой занимается автор. Основная сцена игры — это планета, на гексах которой игрок размещает постройки, перетаскивая туда соответствующие карточки. Каждый ход он получает 1 карту: заготовку для постройки или заклинание. Из этого краткого описания уже формируются первые шаги к пониманию процесса:

  1. Разработка дизайн-документации на игровую механику (строительство и апгрейд зданий);

  2. Создание списка возможностей построек:

    1. Производство ресурсов. Например, +1 энергии / ход;

    2. Случайный дроп. Например, с вероятностью 75% приносит единицу случайного ресурса каждый ход;

    3. Модификаторы (коэффициенты) экономики. Например, +5% скорость роста населения;

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

  3. Создание списка контента (построек);

  4. Формирование структуры Excel / Google (или иной) таблицы, в которую будут записываться данные;

  5. Наполнение таблицы контентом, и его балансировка;

  6. Формирование и проверка данных в собственном формате проекта или в одном из общепринятых форматов передачи данных. Например, JSON, YAML, LUA…

  7. Перенос данных в движок и сборка билда;

  8. Тестирование валидности данных и игрового баланса в игре.

Давайте рассмотрим некоторые пункты подробнее.

Разработку дизайн-документации и проектирование игровых механик я пропущу, так как это тема для отдельного материала.

Рис. 8. Скриншот фрагмента страницы Confluence с описанием планеты и строительства зданий

Рис. 8. Скриншот фрагмента страницы Confluence с описанием планеты и строительства зданий

96На скриншоте выше видно, что для описания одной только механики строительства мы подготовили сразу несколько документов, рассказывающих, что такое планета, зоны и гексы на ней, как разыгрываются карточки, с помощью которых создаются здания, как они размещаются на сцене и так далее. Удобнее разделить огромный документ на небольшие статьи, раскрывающие отдельные элементы механики, соединённые в единую структуру системой ссылок, пронизывающих всё повествование. Благо Confluence, Notion и аналогичные сервисы для ведения проектной и продуктовой документации отлично для этого приспособлены.

Также хорошим тоном можно считать разделение контента и описания механики. Не стоит объединять в одну большую статью то, как работают постройки, какая за ними стоит экономика и сам список этих построек. Однако, это правило не универсально. Оно является следствием простого удобства потребления информации. Если ваша механика ограничена и не требует сотен единиц юнитов, заклинаний, уровней, то бывает удобно расписать всё на одной странице.

Создание списка возможностей построек (модификаторов и игровой экономики), как и сам список всех строений игры, я пропущу, так как этот блок специфичен конкретной игре и в вашем проекте всё может быть совершенно иначе. Главное, что здесь нужно знать, — это логика организации структуры контента, описанная в разделе «Связи и структура данных».

4. Формирование и наполнение таблицы контента

Имея утверждённую продуктовую документацию и контент, геймдизайнер вплотную подходит к вопросу, как теперь описать всё это техническим языком. Из статьи вы знаете, что мы выбрали язык разметки JSON, как один из популярных способов описания контента. Кто-то отмечает его преимущества в привычном синтаксисе, пришедшем из JavaScript, кто-то в простоте освоения. Я же считаю главным плюсом подобных решений наличие готовых бесплатных модулей в ассет сторе для Unity и UE для сериализации данных из JSON / XML. Опыт моих студентов показывает, что научиться писать на JSON — вопрос нескольких дней практики. Это не то же самое, что изучить движок или язык программирования. А вот придумать собственный формат данных и написать для него парсер — отдельная задача для программистов. Более того, каждый новый геймдизайнер и программист на проекте должен будет изучить этот формат, в то время как JSON с той или иной вероятностью уже знает или легко выучит, чтобы в будущем применять и на других проектах.

Рис. 9. Скриншот таблицы ресурсов в Google Таблицах

Рис. 9. Скриншот таблицы ресурсов в Google Таблицах

Чтобы начать работать с постройками, необходимо предварительно описать всё, что с ними связано: ресурсы, карточки, экономику… Пример таблицы со списком ресурсов можно увидеть на скриншоте выше.

Некоторые студии и отдельные разработчики задаются вопросом: зачем тратить время на создание подобных табличек, если можно вручную написать конфигурационные файлы в редакторе Unity / Visual Studio или вообще внести все данные через Scriptable Object Ответ на этот вопрос очень простой.

Если в вашей игре 10 строчек данных, вам не нужны никакие Google Таблицы, JSON и прочие решения. Проблема начинается, когда для описания контента необходимо заполнить сотни конфигурационных файлов, а правка в балансе одного юнита игры затрагивает десятки разных таблиц.

Автоматизация позволяет:

  1. Значительно ускорить описание текущего контента и добавление обновлений  игры;

  2. Реализовать автоматические проверки целостности и валидности данных, чтобы избежать попадание поломанной информации в движок или код игры;

  3. Автоматизировать расчёт баланса;

  4. Автоматически генерировать код, чтобы избежать опечаток, вызванных человеческим фактором.

Рис. 10. Скриншот таблицы построек в Google Таблицах

Рис. 10. Скриншот таблицы построек в Google Таблицах

Обратим внимание, что в таблице построек нет никакой информации о том, что делает та или иная постройка, сколько стоит её возведение и повышение уровня. В первой части статьи подробно рассказано о взаимосвязях таблиц, описывающих постройки и как информация о них разделена на несколько отдельных скриптов.

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

Рис. 11. Скриншот файла локализации

Рис. 11. Скриншот файла локализации

На его примере удобно рассмотреть все преимущества автоматизации, обозначенные нами выше:

  1. Автоматические проверки Google Таблицы защищают геймдизайнера от неверного ввода контента. Ну или по крайней мере предупреждают, если введены некорректные данные. Так, например, при выборе группы, к которой относится локализуемый текст, в поле подгруппа автоматически подгружаются варианты, подходящие для данной группы. К примеру, если мы выбрали группу «Планета», то невозможно будет указать подгруппу ключей, не относящихся к планете. То же самое во всех других таблицах: указывая диапазон чисел, которые может принять значение ресурса в таблице ресурсов (min_value, max_value) можно ввести только рациональное число от минус maxint, до maxint. Ввод иного текста вызовет ошибку, предупреждая геймдизайнера, что вводимые данные могут быть только числом. Как итог, программисты получат от геймдизайнеров гарантированно проверенные и валидные конфиги.

  1. Подсветка ячеек помогает понять, какие данные ещё не заполнены. К примеру, на скриншоте с файлом локализации мы видим, что во второй строке введена заготовка для очередного ключа локализации. Мы выбрали группу «Планета», подгруппу «Здание», но пока ещё не описали, какой именно текст будет записан в данном ключе. Поэтому файлик подсвечивает поле »Item» красным. В одной из строчек ниже текст в поле »Item» заполнен, но не введён перевод, поэтому подсвечено поле »ru», стоящее после »key» (ключа локализации). Если язык оригинала в игре русский, то можно настроить таблицу так, чтобы она подсвечивала все поля на английском, испанском и других языках, для которых геймдизайнеры поменяли что-то в русской локализации. Так мы никогда не забудем отправить на повторный перевод текст, оригинал которого изменился.

Не менее важная часть автоматизации ввода контента — это расчёт баланса. Нормальной является ситуация, когда геймдизайнерам приходится постоянно добавлять новый контент (в нашем случае постройки) и считать с нуля производство, стоимость покупки, улучшения и многие другие параметры. Это не только трата времени, но и ещё одно место, где можно допустить ошибку.

Общий принцип во всех игровых проектах, над которыми я работал, — автоматизируй всё и никогда не пиши код сам. Любая ручная работа — шанс ошибиться.

Рис. 12. Скриншот таблицы стоимости апгрейда построек до заданного уровня

Рис. 12. Скриншот таблицы стоимости апгрейда построек до заданного уровня

На скриншоте выше видно, что часть параметров рассчитывается автоматически. К примеру, мы знаем, что постройка не имеет никаких эффектов, кроме простого производства ресурсов. Допустим, «Планетарный потрошитель» на 1-м уровне производит 1 камня за ход. Наша задача — рассчитать стоимость постройки этого здания. Геймдизайнеры решили, что на 1-м уровне оно будет строиться только из металла. Значит, нам нужно посчитать, сколько металла будет стоить здание, производящее 1 камень. Для этого нам нужно знать две вещи:

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

Я специально описываю процесс баланса поверхностно, так как рассказ о нём — это материал на отдельную маленькую 3-хчасовую лекцию. Наша текущая задача — понять и убедиться, что баланс и математика также могут и должны быть автоматизированы.

5. Валидация и перенос данных в движок

Следующий этап на нелёгком пути геймдизайнера, создающего контент, — как-то закодировать информацию из посчитанных таблиц, чтобы её можно было передать в движок.

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

Обратите внимание на любой скриншот из Google Таблиц с конфигами (рис. 5, 6, 7).

 Последняя колонка всегда содержит ячейку для JSON скрипта.

Рис. 13. Скриншот формулы генерации JSON скрипта

Рис. 13. Скриншот формулы генерации JSON скрипта

Рис. 14. Скриншот итогового JSON скрипта, сгенерированного формулой

Рис. 14. Скриншот итогового JSON скрипта, сгенерированного формулой

Каждая строка с данными в итоге заканчивается ячейкой со скриптом, содержащим записанную в строке информацию. Следующий шаг — проверить, что генерируемый формулой скрипт содержит рабочий код без ошибок. Для этого удобно использовать различные онлайн валидаторы. Их легко найти в поисковике по запросу «JSON online validator».

Рис. 15. Скриншот JSON кода в онлайн парсере, проверяющем его на ошибки

Рис. 15. Скриншот JSON кода в онлайн парсере, проверяющем его на ошибки

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

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

Итоговый алгоритм автоматизации работы с контентом можно кратко изобразить на схеме:

Рис. 16. Схема работы с контентом

Рис. 16. Схема работы с контентом

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

Рис. 17. Скриншот прототипа стратегии Тени Возмездия

Рис. 17. Скриншот прототипа стратегии Тени Возмездия

6. Вместо выводов

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

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

© Habrahabr.ru