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

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

Этот туториал — часть серии Начало работы — День 5, Неделя 1.

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

Вчера мы собрали папку Враги и наполнили её - для каждого врага это будет игровой объект с одинаковыми полями: health, speed, damage и size. Ребаланс происходит в приложении, и с точки зрения гейм-дизайна всё отлично.

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

Дизайнер обновляет speed врага Посетитель-ходячий до 60. Программист, работая по старой заметке, работает со значением 30. Дизайн и код незаметно расходятся друг с другом, вызывая бесконечные споры о важных деталях. Это классический рассинхрон, как в известной басне про лебедя, рака и щуку.

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

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

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

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

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

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

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

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

{
  "title": "Mall walker",
  "values": {
    "props": {
      "damage": 8,
      "speed": 30,
      "health": 60,
      "size": 1
    }
  }
}

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

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

Теперь сторона движка - для примера возьмём реальный Godot-проект ("Dead Mall"): сцена с Хлоей и тремя врагами (Посетитель-ходячий, Посетитель-ходячий (бронированный), Обжора из фуд-корта), которые преследуют её. У каждого врага есть экспортируемый data_path, указывающий прямо на его .ima.json, а скрипт врага считывает speed и size обратно в сцену:

@export_file("*.json") var data_path: String

func _reload():
    var file = FileAccess.open(data_path, FileAccess.READ)
    var json_text = file.get_as_text()
    file.close()

    var json = JSON.new()
    json.parse(json_text)

    var props: Dictionary = json.data.get("values", {}).get("props", {})
    speed = props.speed
    scale = Vector2(props.size, props.size)

func _ready():
    _reload()

Теперь Godot видит speed = 30 и size = 1 для Посетителя-ходячего - те же числа, что задал гейм-дизайнер в приложении. Враги преследуют Хлою, так что числа видно вживую: поднимите Скорость или Размер в IMS Creators, сохраните и перезагрузите сцену - и враг действительно побежит быстрее или станет крупнее.

Файлы .ima.json, загруженные в Godot, со значениями врагов, видимыми в сцене

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

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

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

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

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

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

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

Готовы? Задайте полям настоящие типы - следующий пост в этой серии.


FAQ

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

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

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

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

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

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

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

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

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

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

Поделиться:
Назад к блогу