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

Вечно воюете с тем, что в доке написаны одни данные, а движок видит совсем другие? Рассказываем, как один файл может навсегда избавить вас от рассинхрона

Проблема: дизайн и код расходятся

Давайте создадим коллекцию Cards и наполним её - для каждого предмета это будет игровой объект с одинаковыми полями: typepowerdurability и weight. Ребаланс происходит в приложении, и с точки зрения гейм-дизайна всё отлично.

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

Дизайнер обновляет Fire Extinguisher до power = 8. Программист, работая по старой заметке, работает со значением 5. Дизайн и код незаметно расходятся друг с другом, вызывая бесконечные споры о важных деталях. Это классический рассинхрон, как в известной басне про лебедя, рака и щуку.

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

В IMS Creators есть возможность выгружать данные в кастомных форматах под конкретный движок, но об этом мы подробно поговорим позже. Сейчас рассмотрим самый простой и наглядный путь: разместим проект IMS Creators Desktop прямо в директории с Godot-проектом. Тогда дизайн и код окажутся по соседству, а данные лягут на диск рядом с движком - и движку не придётся долго искать, откуда их читать.

Почему именно JSON решает данную проблему

Игровой объект не зашит намертво внутри движка или редактора. На диске это обычный файл - например, Cards/Fire Extinguisher.ima.json. По сути, мы имеем дело с чистым, структурированным JSON-форматом. А такой открытый JSON - это самый быстрый и прямой мост между дизайном и кодом:

  • JSON - обычный текстовый формат, который умеет читать любая программа - без специализированного экспорта и без привязки к платформе.
  • Он структурирован - числа остаются числами с теми же именами полей, что вводил дизайнер (typepowerdurabilityweight), а не обычным текстом, который нужно парсить.
  • Он обновляется в момент сохранения - правите в IMS Creators, и вместе с вами меняется тот самый .ima.json, который загружает движок.

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

Откройте папку проекта и прочитайте данные полей

Посмотрим, что реально лежит на диске. Откройте папку проекта и откройте Cards/Fire Extinguisher.ima.json в любом текстовом редакторе. Это JSON-документ с небольшим служебным блоком вверху - id, какие-то blocks и title элемента. Что важно для движка - секция values -> props, которая хранит именно те поля, что вы ввели:

{
 "title": "Fire Extinguisher",
 "values": {
 "props": {
 "type": "Weapon",
 "power": 8,
 "durability": 4,
 "weight": 6,
 "description": "Foam canister that douses flames and foes"
 }
 }
}

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

Загрузите тот же файл в Godot

Теперь сторона движка - для примера возьмём реальный Godot-проект ("Godot Mall"). Игра оборачивает JSON в небольшой ресурс CardData, чей load_from_file() открывает тот же путь, куда пишет приложение, и читает поля прямо из values.props:

var data: Dictionary = json.data
var props: Dictionary = data.get("values", {}).get("props", {})
_type = str(props.get("type", ""))
_power = int(props.get("power", 0))
_durability = int(props.get("durability", 0))
_weight = int(props.get("weight", 0))

Теперь Godot видит type = "Weapon"power = 8durability = 4weight = 6 - те же числа, что задал гейм-дизайнер в приложении. Привяжите это к сцене предмета - и каждый предмет спавнится из того самого файла дизайна. Меняете значение в IMS Creators - и при следующем запуске Godot читает новое. Дизайн и код наконец-то работают с одними и теми же данными.

Результат: один источник истины

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

  • Никакой перепечатки. Никто заново не вводит power и не извлекает его из предложения. Движок читает поле напрямую.
  • Никакого расхождения. Ребаланс в приложении мгновенно становится информацией на диске, так что движок не может показывать устаревшие числа.
  • Дружелюбно к git. Раз .ima.json - это обычный текст, каждое изменение баланса можно просмотреть и откатить.

Разрозненные заметки стали коллекцией, коллекция стала простыми файлами, простые файлы стали данными, которые загружает движок. Весь цикл от «идеи в голове дизайнера» до «числа в игре» - теперь одна папка, которую можно открыть.

Дальше: связываем персонажей и способности

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

Готовы увидеть свои числа в работающей игре? Скачайте IMS Creators, откройте коллекцию проекта на диске (в реальном проекте она лежит в папке Cards), прочитайте один .ima.json в текстовом редакторе и загрузите те же поля в Godot уже сегодня: ims.cr5.space/desktop.


FAQ

Что именно находится в файле .ima.json?

Это обычный JSON-документ одного элемента. Помимо небольшого служебного блока (id и blocks), он содержит title элемента и, в разделе values -> props, введённые структурированные поля - например, typepowerdurability и weight. Всё это стандартный JSON, который можно открыть в любом редакторе и игровом движке.

Нужно ли экспортировать что-то, прежде чем движок сможет это прочитать?

Нет. Файл .ima.json уже лежит на диске как обычный JSON в момент сохранения элемента. Чтение не требует шага экспорта - движок может загрузить файл напрямую. Экспорт в один клик - это отдельная тема дальше в серии.

Что, если мой движок ожидает другую структуру, чем values.props?

Не проблема. Файл - обычный JSON, так что вы можете разбирать его так, как нужно вашему пайплайну: пример выше читает values.props, но тот же файл легко сопоставить с любой нужной движку структурой всего парой строк кода. А если хочется совсем без парсинга, прямо в IMS Creators есть возможность настраивать формат экспорта под конкретный движок - тогда данные будут приходить уже в нужной структуре.

Заменяет ли это полноценный контроль версий?

Это его дополняет. Так как файлы - обычный текст, их можно коммитить в git и делать откат каждого изменения. Контроль версий - ваша страховка, открытый JSON - то, что изначально держит дизайн и код в согласии.

Перезапишет ли IMS Creators мой файл, если я отредактирую его вне приложения?

Нет - именно данные с диска служат источником правды, поэтому IMS Creators подхватит изменения, которые вы внесёте в файл со стороны. Тем не менее, пользоваться редактором IMS Creators намного удобнее: он понимает структуру элемента и аккуратно вносит правки, не рискуя случайно повредить служебные поля.

Назад к блогу