Как сделать игровые конфиги силами одного гейм-дизайнера

и не умереть под ворохом данных.

Неравная битва гейм-дизайнера и игровых конфигов. Конфиги побеждают.<br /> (с) <a href="https://api.dtf.ru/v2.8/redirect?to=https%3A%2F%2Fwww.instagram.com%2Fhocopor%2F&postId=74093" rel="nofollow noreferrer noopener" target="_blank">Носорог</a>
Неравная битва гейм-дизайнера и игровых конфигов. Конфиги побеждают.
(с) Носорог

Привет! Меня зовут Сергей, я работаю гейм-дизайнером и люблю изобретать велосипеды. Так уж получается, что обычно я работаю в маленьких командах, даже когда в больших компаниях. И в маленьких командах почти невозможно отвлекать разработчиков на такую ерунду как админка для создания и хранения конфигов. Вот только игры надо как-то конфигурировать, где-то хранить все эти несметные тыщи игровых сущностей и желательно в человекочитаемом виде. Нужны конфиги! :(

Ну, например, у меня в игре есть 42 посоха, 11 зелий восстановления маны, 33 зелья лечения, 100 мечей ... и всё это с разными параметрами и собираются из фрагментов, каждый фрагмент обладает своими параметрами и добывается из сундуков, сундуки лежат в данжене, а данжен расположен на локации, в лесу потерянных гейм-дизайнеров. Все это добро надо как-то внятно описать, иметь возможность удобно редактировать (удобство редактирования это супер важно!) и интегрировать в игру.

Видел случай, когда ГД, бедолага, руками, путаясь в скобках, собирал json, а потом мы правили баланс и выяснялось, что нужно поправить примерно пару-тройку десятков файлов. К слову, именно у этого ГД я подсмотрел, а потом развил идею с конфигами в гуглодоках (Никита, привет!). Я слишком ленив для такой работы, хочется больше времени заниматься ГД, а не правкой бесконечной кучи файлов.

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

Так как же я делаю конфиги?

В гуглотабличках! Использую всю силу луны и гугля. Делаю таблицы с сырыми игровыми данными, в тех же таблицах заворачиваю данные в промежуточный, мною изобретённый формат (подозрительно похожий на json), потом забираю из гуглотаблицы питонячим скриптом (опять же, минутка рекламы, самописным!), который конвертит мой формат в полноценный json хоть и с некоторыми ограничениями и сохраняет на рабочий комп. Полученные файлы можно заливать на сервер или включать в билд. Кстати, на сервер мой скрипт заливать тоже умеет :)

Про сам промежуточный формат и как он конвертится к json можно посмотреть в этой краткой инструкции. Совсем кратко я расскажу ниже.

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

Например, строка:

9.1, 6.0, 6 | zero = 0, one, two = {2, dva}, tree = 3 | a, b, f

Будет конвертирована в json как:

[ [ 9.1, 6, 6], [ "one", { "zero": 0 "two": [2, "dva"], "tree": 3 } ], [ "a", "b", "f"] ]

Очевидно, что внутри гуглотаблички первую строку собрать сильно-сильно проще и она более человекочитаемая.

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

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

Если я хочу продавать меч тысячи истин не только за золотые, а за золотые, жабьи лапки и два хвоста крыс, то мне нужен список. Что бы не помечать отдельно когда список нужен, а когда нет, то проще всегда заворачивать словари в список, а программистов поставить перед фактом (аргументируя тем, что раз они не захотели писать админку для конфигов, то пусть и не возмущаются). У меня прокатило :)

Отдельно на конвертор в json можно посмотреть в моём репозитории. Там же есть наглядные примеры.

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

В чем выгода?

Простота и быстрый старт! Можно сразу (сразу!) начать работу и без дополнительных телодвижений получить приемлемый результат. И не нужно идти на поклон к программистам!

Гейм-дизайнер просит программиста об админке для конфигов.<br /> (с) <a href="https://api.dtf.ru/v2.8/redirect?to=https%3A%2F%2Fwww.instagram.com%2Fhocopor%2F&postId=74093" rel="nofollow noreferrer noopener" target="_blank">Носорог</a>
Гейм-дизайнер просит программиста об админке для конфигов.
(с) Носорог

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

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

В моём случае с таблицами активно работают ПМ и тестировщик, не отвлекая меня, когда нужно внести изменение для тестов или для добавления нового контента.

Кстати, у меня экспорт настроен через телеграм бота . Это тоже есть в репозитории.

В чем недостатки?

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

Нет версионности, сложно контролировать целостность документы, иногда отваливаются кастомные гуглоформулы. Сложно (но можно!) иметь разные конфиги если нужно поддерживать сразу несколько серверов, особенно с разными версиями конфигов.

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

Как готовить конфиги?

Конфиги надо готовить. Совсем простые, формата "ключ = значение" делать легко, достаточно двух колонок: в одной ключи, в другой, как не сложно догадаться, значения. Навороченные уже сложней, нужна структура, понимание как оно будет заворачиваться с промежуточный формат и как оно будет разворачиваться в конечный json.

Пожалуй, проще всего разобрать на примерах.

Совсем простой случай

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

Первая строка зарезервирована под заголовки либо служебные метки. В нашем случае, это системные метки "key" и "value", по ним конвертор понимает что из этого нужно сделать словарик, и будут экспортированы только эти два столбца.

В остальных столбцах может быть что угодно, например комментарии или расчеты. Столбцы с данными могут даже не находится рядом! В строках ниже идут данные, в key находятся название переменных, в value — значения. На выходе будет валидный json документ.

ВАЖНО! Между строками не должно быть пробелов. Они создадут пустые словари!

Можно заполнять таблицу по другому. В первой строке расположены ключи, в строках ниже — значения. В общем случае строк со значениями может быть много. Важный момент, в странице для экспорта ключи должны быть именно в первой строке! Если хочется дополнительных данных, то всегда можно завести дополнительные страницы с "калькуляторами", там делать все расчеты, а результат складывать во вкладку для экспорта.

Вот так мы плавно подошли к чуть более сложному способу хранения данных.

Внутренний редактор ломается на символе "#". Пример альтернативного заполнения во вкладке @general2.json

Более сложный случай

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

Сам пример во вкладке @swords.json

ВАЖНО! В станице экспорта между строками не должно быть пробелов!

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

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

Каждая спец. способность состоит из имени абилки и силы её действия, записей может быть несколько. На каждую абилку придется заводить свою строку. Тут важно разделять предметы пустыми строками и следить чтобы запись для одного предмета не выползли в зону другого. Сами же абилки задаются вполне интуитивно, в первом столбце имя, во втором сила. А вот собираются в одну строку уже немного сложней (помним, у нас должна быть всего одна строка на один предмет). Для этого у меня подготовлены кастомные функции в гуглотаблицах, самая востребованная — toConfigBlock. Описание и примеры как она работает можно найти на странице калькулятора если ткнуть в формулу под заголовком extra_power.

К слову, вложенная структура может состоять из произвольного числа столбцов. Это не усложнит работу.

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

Названия абилок и ресурсов в цене должны храниться в другом конфиге или уже захардкожены в игре.

Общие рекомендации по работе с конфигами

У меня есть набор рекомендаций, которым я следую при работе с конфигами. Пройдусь по ним кратким списком:

  • Вкладки на экспорт я отмечаю красным, рабочие документы — зеленым, справочники — голубым;
  • Ячейки которые вбиваются руками — зеленые, собирающиеся при помощи формул светло-красные, ячейки в которых находится формула — красные;
  • Обязательно оставлять себе будущему комментарии, это супер важно! Тот будущий чувак обладает очень плохой памятью и иногда страдает слабоумием, пожалейте его!
  • Данные, которые будут использованы более чем в одном документе, лучше хранить в отдельной таблице и подгружать в рабочие документы как справочник, так можно избежать множественных редактирований по всем документам если у вас изменились базовые константы на которых строится баланс.

Начало работы

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

Можно и не копировать, а создать свой, но советую таки скопировать, там уже подготовлена база, в том числе и из кастомных функций, которые знатно облегчают жизнь при работе со сложными конфигами. Каждая функция документирована. Не ленитесь залезть в редактор скриптов (Tools \ Script Editor) и посмотреть что там есть. Из одного документа в другой функции можно копировать либо подключать как библиотеку.

Внимательно посмотрите на файл с настройками для конфигов. Стоит начать со вкладки "read.me!" и пройтись по остальным вкладкам читая комментарии. Я старался подробно всё описать.

ВАЖНО! Названия страниц, которые экспортируются, должны начинаться с символа собаки "@", иначе они не будут забираться скриптом экспорта.

Итак, конфиг готов. Пойдем дальше!

Настроить доступ к гуглопапке с конфигами

Я делал по этой инструкции. Там всё просто.

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

Простой вариант

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

Да, подразумеваю, что читатель либо немного знает питон либо под рукой есть тот кто его знает. Тут всё реально просто. Правда-правда!

  1. Склонируйте или скачайте файло из репозитория
  2. Установите все зависимости для работы срипта из файла config/requirements.txt
  3. Отредактируйте bot/local_lite.py дописав туда ID таблицы с конфигом и путь до файла с токеном авторизации к гуглодрайву.
  4. Запускайте скрипт, он должен достучаться до гуглтаблиц и сохранить все вкладки которые начинаются с "@".

ID таблицы можно найти в урле до этой самой таблицы, это многосимвольный фрагмент, что-то типа "1cbNBO69iUcX7CIx6E55Hy1XDdQ9IJp9mWF3u6g8CDpY"

Сложный вариант

Сложный вариант будет описан в следующих сериях :)

Заключение

Это было кратко и сумбурно, но, надеюсь, понятен основной подход к созданию конфигов. Резюмирую в несколько строчек:

  1. Использую гуглотаблички;
  2. Собираю все данные в плоскую таблицу, используя свой промежуточный формат;
  3. Забираю данные из таблиц питонячим скриптом;
  4. Преобразую в json;
  5. Сохраняю на локальную машину или сразу на сервер.

Спасибо за внимание! Надеюсь, было полезно :)

ЗЫ: Во избежание душевных травм заранее предупреждаю, я не настоящий сварщик, код пишу от любви к велосипедостроению и безысходности :)

4444
42 комментария

За то, что запилили решение под свои нужды полностью сами — респект и уважуха. Вообще молодец. И в этом смысле очень жизнеутверждающая статья.

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

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

3
Ответить

Спасибо! 
Этот велосипед тоже не сильно долго писался. Суммарно, вместе с разворачиванием в докере и прикручиванием к телеграму около 5-8 рабочих дней силами одного меня.

Статей именно по ГД довольно много, а вот по деталям реализации и инструментам почти нет.

Ответить

рекламировать упрощение работы ГД через гуглодокументы, вставляя нерабочие ссылки на эти гуглодокументы. Ирония :)

2
Ответить

Спасибо, поправил. Внутренний редактор ломатеся на символе "#" в адресной строке.

Ответить

Отличная статья как делать не надо )))

1
Ответить

А как надо делать? :)

Ответить

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

1
Ответить