Хабрахабр

[Из песочницы] Упрощаем сборку билдов в Unity3D

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

Выглядит он примерно так:

А зачем, почему, где взять и как пользоваться я расскажу ниже.

Боль

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

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

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

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

В итоге подготовка каждого нового билда со временем становится похожей на это:

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

Да и вспомни все манипуляции через месяц-другой. Поначалу еще может и ничего, но быстро утомляет.

Какие ваши варианты?

Интернеты и народная мудрость предлагают нам следующее:

  • Решать проблему на уровне проектов. Реализации этого подхода могут быть разные: отдельные проекты с общим кодом и ассетами, подмодули в гите, ветки в гите и тому подобное.
    Всерьез рассматривать такое я не могу. Городить огород ради подмены иконки, а потом еще и поддерживать. Совсем на любителя. Но видел своими глазами, поэтому не мог не упомянуть.
  • Bash/bat/python/… скрипты.
    Уже теплее. Но требует дополнительных знаний и сложно назвать кроссплатформенным (часто часть команды разрабатывает под макосью, часть под виндой). Опять же скрипты нужно поддерживать, а на это не всегда есть время.
  • Editor скрипт в самом юнити.
    Совсем тепло. Воротим проект внутри юнити как хотим. А если использовать статик метод и BuildPipeline, то можно и CI настраивать. Как правило в статьях про юнити и CI подобное и описывается. Однако здесь сохраняется один недостаток предыдущего подхода: это код и его надо писать, дописывать и поддерживать. Нам нужно нечто подобное, но без необходимости на каждую платформу и конфигурацию лезть в код.

Пишем свой плагин, с UI и феями. Решено.

Нам нужен план

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

  1. Хранение настроек проекта под каждую конфигурацию (здесь можно перейти на терминологию самого плагина и называть их вариантами). Желательно реализовать наследование вариантов. Например для всех standalone билдов настройки проекта будут общие, а вариантов друг от друга будут отличаться только иконкой и именем проекта. Вполне логично решать это наследованием, а не копипастой.
  2. Наглядное представление, что поменяно в конкретном варианте и чем он отличается от текущих настроек проекта.
  3. Объединение вариантов в коллекции. Чтобы была возможность одной кнопкой или одним статик методом собрать сразу некоторое подмножество билдов.
  4. Упаковка билда в архив. Это опциональное требование. В случае использования плагина в паре с билд сервером этот пункт можно реализовать на его стороне. Но жизнь упростить хочется упростить всем, а билд сервер есть не всегда.
  5. Перемещение файлов на разных стадиях сборки. Например нужно подменить какой-нибудь ассет (иконку, сплэш, сцену и т.д.) перед сборкой (подмена ассетов при этом должна быть обратима, сборка билда не должна изменять текущее состояние проекта). Или переименовать в готовом виндовом билде _Data в Data (весьма раздражающее поведение юнити, без этой манипуляции нельзя переименовать экзешник). Или опять же в виндовых билдах расставить нужные StreamingAssets. Делать это нужно до упаковки архива. А потом например перетащить все архивы куда-то в одно место.
  6. У юнити проектов огромное количество настроек и нам совершенно не хочется писать свой UI для них всех. Тем более, что он уже есть в самом юнити. Поэтому настройки проекта будем менять как обычно, а в плагине просто следить за изменениями настроек и сохранять в нужные варианты.
  7. Хотелось бы хранить каждый вариант в отдельном файле, чтобы была возможность таскать их между проектами.
  8. Желательно чтобы работало с юнити начиная с версии 5.6. Есть некоторое количество легаси проектов с большим количеством возни вокруг билдов.

Погнали

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

И первым делом при инициализации мы сохраним эти настройки в директории плагина (к слову это BuildVariants в корне проекта). Все настройки юнити хранит в директории ProjectSettings в корне проекта. Далее нам нужно их как-то хранить. Это потребуется для того чтобы мы могли отслеживать все последующие изменения. И в момент его активации просто подменять их целиком. Самым простым было бы просто копировать ProjectSettings в отдельное место под каждый вариант. Но это полностью лишает нас возможности наследовать варианты. Идея вполне рабочая и поначалу я рассматривал именно ее. Значит нам нужно научиться читать и писать содержимое ProjectSettings. И если мы уже пишем плагин, то будем сразу делать красиво.

А хранятся там самые обыкновенные ассеты, которые сериализуются и десереализуются штатным образом. Для этого нужно понять, что же такое там хранится. Это уже что-то. Это значит, что мы можем прочитать их через AssetsDatabase и получить SerializedObject. Потом при сборке или активации варианта мы собираем изменения по всей цепочке наследования, применяем их к "снимку" и подсовываем в ProjectSettings. Потому что мы можем пройтись по всем SerializedProperty, отследить их различия с исходным "снимком" и сохранить их в варианте. Вуаля.

Не могу сказать, что это какие-то фатальные недостатки, Все это делалось и не раз в многочисленных editor скриптах. Но у SerializedProperty есть некоторые неприятные особенности: способ хранения значений (все эти intValue, floatValue, isArray), отсутствие нормального механизма их сравнения (есть два родственных объекта, как нам понять все ли SerializedProperty у них имеют одинаковые значения) и парадоксальное (а может и нет) отсутствие аттрибута Serializable (а значит для сохранения нам нужен будет какой-то враппер). Но хочется все же чего-то более простого, понятного и универсального.

Пусть он работает только на проектах с текстовой сериализацией ассетов (а кто-то еще пользуется бинарной?). А может быть мы немного ограничим применение нашего плагина? Можно взять библиотеку YamlDotNet и с ее помощью парсить и сохранять настройки. Тогда все ProjectSettings будут храниться в виде yaml документов. Так даже нагляднее. Заодно и свои конфиги хранить в yaml. Нужно только дописать немного расширений для диффов yaml документов и их объединения.

Немного возни с editor window (даже описывать не хочу, надеюсь, что с UIElements жить станет проще и веселее), сбор всего в кучу и готово.

Результат

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

  • Варианты добавляются в выбранную коллекцию при помощи галочки слева от имени.
  • Build path нужно указывать с расширением. Не нашел полного перечня расширений под все возможные BuildTarget, поэтому пока оставил так.
  • Перемещение файлов наследуется так же как и настройки.
  • Не реализован дифф массивов. Поэтому если в отнаследованном варианте вы что-то изменили в списке сцен, то весь список будет заменен целиком. По хорошему надо будет доработать.
  • Actual project settings diff — это список всех отличий выбранного варианта от текущих настроек проекта. При этом если выбран активный вариант, то изменения в текущих настройках можно отменять.
  • Variant settings — это соответсвенно индивидуальные изменения в выбранном варианте, без учета родительских изменений.
  • При активации варианта все несохраненные изменения в настройках проекта будут утеряны.
  • Если в настройках проекта есть несохраненные изменения, то любая попытка собрать билд через плагин закончится эксепшеном. Потому что см. выше, а как мы помним "сборка билда не должна изменять текущее состояние проекта".
  • Плагин можно использовать вместе с билд сервером. Для этого есть статик методы: BuildVariants.Controller.BuildController.BuildAll, BuildVariants.Controller.BuildController.BuildCollection (с именем коллекции в параметре -collection) и BuildVariants.Controller.BuildController.BuildVariant (с именем варианта в параметре -variant). Выглядеть это будет примерно так:
    $unity_path -batchmode -projectPath $project_path -quit -logfile -executeMethod BuildVariants.Controller.BuildController.BuildCollection -collection standalone
  • Для возможности архивирования плагин имеет еще одну зависимость в виде библиотеки DotNetZip.
  • Плагин написан в условиях небольшого рабочего затишья, поэтому пока еще не представилось возможности испытать и закалить его в ожесточенных боях. Могут быть баги, возможно даже плохие и неприятные.

Планы на будущее

Чуть позже хочу применить UIElements для свежих версий юнити, надеюсь это позволит сделать все более красиво и удобно. Конечно же хочется кормить плагин фичами до бесконечности, но пора возвращаться к реальным проектам, а заодно тестировать имеющийся функционал. Может быть общественность подскажет что-нибудь еще по функционалу или юзабилити. Хочется еще прикрутить версионирование и некое подобие переменных окружения.

Легких билдов вам! Поэтому буду рад любым пожеланиям, замечаниям, вопросам и баг репортам.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть