Неделя 28 (апрель 3): Редактор конфигов и истории моей молодости.

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

В общем, оказалось все относительно просто, хоть и закопано в совершенно неприличные глубины. И даже больше, все оказалось намного лучше чем могло бы быть. Оказалось что к месту предполагаемого хранения конфигов можно достучаться с клиента запросом не только на чтение, но и на запись. То есть я могу соорудить какую-нибудь админку, которая нажатием кнопки сама выгрузит необходимые данные на сервер.

Значит можно заняться созданием такой кнопки и приведением в порядок ядра игры.

***

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

Это вроде бы очень удобно, но в определенный момент начинает вызывать проблемы. Например когда один синглтон зависит от другого. Есть вот контроллер рецептов. Пока вся логика игры находится у меня на клиенте, при запуске он должен высчитывать прошедшее время и завершать рецепты, время которых пришло. Если рецепт относился к постройке, в которой встречаются автоматические рецепты, и в этой постройке есть свободные слоты, то контроллер рецептов должен запустить производство нового автоматического рецепта… но стоп, разве мы уже знаем есть ли постройке свободные слоты? Ведь постройки относятся к другому контроллеру — контроллеру построек. А он может внезапно взять и загрузиться после контроллера рецептов а не до.

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

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

Ну и еще одна причина: Юнитя пропагандирует использование скриптбл обжектов в качестве синглтонов. И в этом есть определенный смысл.

  • Во-первых юнитя сама следит за тем, чтобы скриптбл обжект был «на сцене» в единственном экземпяре.
  • Во-вторых скриптбл обжекты могут быть доступны не только внутри игры, но и снаружи как простые настраиваемые объекты редактора Юнити.
  • Во-третьих они сами сохраняют все происходящие с хранящимися в них данными изменения.
  • В-четвертых… есть такая проблема: если все данные загружаются в синглтонные объекты, их надо как-то протаскивать между сценами. Например мы в сцене загрузки загружаем все данные с сервера, а потом переходите на сцену игрового мира, а потом на сцену боя, а потом еще куда-нибудь.
    • Тут во-первых надо следить за сохранностью этого синглтона.
    • А во-вторых нельзя сразу войти в сцену боя — в ней же нет своего собственного создателя синглтонов.

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

***

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

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

С Одином все оказалось значительно проще.

  • Во-первых, у Одина есть примеры покрывающие почти все возможные варианты.
  • Во-вторых, у Одина есть чатик поддержки где можно взрывать мозг разрабам в прямом эфире.

У Одина есть несколько концептуально сложных вещей, к которым надо просто привыкнуть… они собственно и у Юнити есть, но примеры значительно облегчают это дело.

***

У меня есть необычайно грустная история про редакторы конфигов. Дело в том, что разработчики, тем более небольшие, очень мало внимания уделяют вопросу того, как игра будет наполняться данными. Это приводит к тому, что самый неопытный начинает заниматься безумной объезьяньей работой вместо того чтобы получать опыт. Более опытные этой фигней не занимаются просто по причине здоровой дедовщины.

Последним моим большим проектом в котором надо было что-то забивать было Рыбное Место 2. Там даже был отдельный программист который клепал нам админку, но моих требований к фукнционалу он выполнить так и не смог. В результате админка использовалась только для манипуляций с пользовательскими данными — для начисления наград за ивенты и прочее. Сами же игровые данные я набивал в эксле, где у меня были шаблоны SQL-запросов. Табличку экселя я переносил в Блокнот, автозаменой удалял пробелы и табуляции и через SQLMyAdmin выливал на сервер. Да конфиги там хранились в mySQLной базе.

Но ведь смотреть только на пользовательские данные все-таки мало. Иногда ошибки править надо, иногда что-то подкручивать, иногда просто понять почему произошло одно а не другое. И в порыве отчаяния я сотворил админку сам. На OpenOffice Base. Это такой бесплатный аналог MS Access позволяет сделать клиент для работы с базой данных почти на любой вкус и цвет. В общем в своей жизни…

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

А еще более до того… во времена моей работы в G5 всякие конфиги для тогдашних игрушек передавались в виде XMLек, а редактировались редакторе XMLSpy. Не скажу откуда у них был XMLSpy но очень рекомендую погуглить что это за тулза и сколько она стоит. Серьезно. Главная фишка этого редактора заключается в том, что как в и Экселе в нем можно переходить от ячейке к ячейке по вертикали.

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

Более того, формат XML или JSON подразумевают возможность описать внутри объекта вообще любой другой объект. Если конечно строгие правила не описаны и не работает какой-нибудь механизм контроля их исполнения на этапе ввода. Тут зарыта довольно сложная разница между привычным мне со времен хранения конфигов в базах данных и действительно удобным способом работы с данными.

Например есть у нас постройки рецепты. Так или иначе все рецепты принадлежат постройкам. Их можно перечислить как список свойств постройки или в отдельной таблице указывая ссылку на постройку. Первый вариант — это вариант XML или JSON. Второй — это базы данных.

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

***

И вот, я занялся разработкой админки. Логика устройства админки такова: есть класс, который описывает окно и список объектов в древовидном списке… что-то типа «Области навигации» в проводнике. Кто бы мог подумать что у этой штуки есть собственное название. При нажатии на элемент списка в основную часть окна начинает выводиться визуальная часть этого элемента. И описывается это визуальная часть в коде класса элемента. Там можно в том числе вывести как-нибудь связанный экземпляр какого-то третьего класса и его отображение в свою очередь будет описано где-то там, в коде этого третьего класса.

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

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

И только после всех этих манипуляций можно будет вернуться к меню постройки.

Тут можно видеть собственно работу админки, на сколько я успел ее сделать. Слева «Область навигации». Туда можно пихнуть кнопку для любого объекта, а можно сделать дерево.

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

Потом идет собственно папка с артикулами. В ней я отображаю класс, хранящий List артикулов… должен же он где-то храниться. Вообще папки с отображением списков я собираюсь использовать для каких-то массовых действий типа сортировки и активации/деактивации — это тоже важная история экран не должен засоряться не использующимися в работе объектами, а объекты должны располагаться в удобном, о чем-то говорящем порядке. Звучит как баловство, но это действительно важно, да и по сравнению со всем остальным делалось реально совсем недолго.

При установке иконок почему-то ломается отображение иконок в «Области навигации».

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

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

Это все конечно далеко от идеала XMLSpy, значительно ближе к тому, с чем можно работать не испытывая боль. А при малейшем проявлении неудобства любую проблему можно довольно быстро решить, благо навык уже есть. Например можно сделать клонирование объектов, включая все дочерние. Можно сделать копирование и вставку свойств. В общем, очередной раз Один оказался очень крутым и полезным, а по результату этой работы я смогу выпилить из своего проекта плагины сохранения и загрузки гуглодоков.

Оглавление