[Из песочницы] Упрощаем сборку билдов в Unity3D
Совсем недавно я осознал, что с ростом числа готовых проектов все больше времени приходится посвящать сборке билдов. Нельзя сказать, что юнити как-то сильно усложняет этот процесс, но уж точно и не упрощает. Особенно когда каждый проект собирается под несколько платформ да еще и в разных конфигурациях. В принципе проблема не нова и имеет множество разных решений. Но по ряду причин я остановился на написании собственного плагина.
Выглядит он примерно так:
![[Из песочницы] Упрощаем сборку билдов в Unity3D [Из песочницы] Упрощаем сборку билдов в Unity3D](https://habrastorage.org/webt/da/_r/yz/da_ryzek6oeheil25xrraw_m8xo.png)
А зачем, почему, где взять и как пользоваться я расскажу ниже.
Боль
Первый дискомфорт как правило ощущается, когда к релизу готовится более одной платформы. Выясняется, что в юнити какие-то настройки проекта общие для всех платформ, а какие-то нет. И это нужно всегда помнить и учитывать. Но в целом жить можно.
Потом обнаруживается, что нет возможности сохранять разные настройки в рамках одной платформы. Простой пример: билд один, а площадок, на которых он будет распространяться, много. И под каждую нужно что-то свое. Частично эта проблема решается выносом настроек в рантайм конфигурацию в StreamingAssets. Но лишь частично, ибо так можно конфигурировать только происходящее уже внутри игры. Иконку таким способом не подменишь. Иконку каждый раз нужно менять самому. Кодом ли, руками ли, но самому. Да и в какой-то момент, ответственный менеджер, которого вы месяц обучали работать со StreamingAssets уходит в отпуск, а билды нужны вчера.
Еще позже вы узнаете, что планируется отдельная корейская версия, там вообще все должно быть иначе и без #ifdef тут уже не обойтись.
А потом вам говорят, что игру нужно запускать на мобильные платформы, плейстейшн, нинтендо и стиральные машины.
В итоге подготовка каждого нового билда со временем становится похожей на это:
![]()
Проверить показания приборов, включить тумблер слева, повернуть рычаг справа, закрутить красный вентиль. Ничего не перепутать и не забыть.
Поначалу еще может и ничего, но быстро утомляет. Да и вспомни все манипуляции через месяц-другой.
Какие ваши варианты?
Интернеты и народная мудрость предлагают нам следующее:
- Решать проблему на уровне проектов. Реализации этого подхода могут быть разные: отдельные проекты с общим кодом и ассетами, подмодули в гите, ветки в гите и тому подобное.
Всерьез рассматривать такое я не могу. Городить огород ради подмены иконки, а потом еще и поддерживать. Совсем на любителя. Но видел своими глазами, поэтому не мог не упомянуть. - Bash/bat/python/… скрипты.
Уже теплее. Но требует дополнительных знаний и сложно назвать кроссплатформенным (часто часть команды разрабатывает под макосью, часть под виндой). Опять же скрипты нужно поддерживать, а на это не всегда есть время. - Editor скрипт в самом юнити.
Совсем тепло. Воротим проект внутри юнити как хотим. А если использовать статик метод и BuildPipeline, то можно и CI настраивать. Как правило в статьях про юнити и CI подобное и описывается. Однако здесь сохраняется один недостаток предыдущего подхода: это код и его надо писать, дописывать и поддерживать. Нам нужно нечто подобное, но без необходимости на каждую платформу и конфигурацию лезть в код.
Решено. Пишем свой плагин, с UI и феями.
Нам нужен план
Для начала нужно определиться что мы собственно хотим получить и зачем.
- Хранение настроек проекта под каждую конфигурацию (здесь можно перейти на терминологию самого плагина и называть их вариантами). Желательно реализовать наследование вариантов. Например для всех standalone билдов настройки проекта будут общие, а вариантов друг от друга будут отличаться только иконкой и именем проекта. Вполне логично решать это наследованием, а не копипастой.
- Наглядное представление, что поменяно в конкретном варианте и чем он отличается от текущих настроек проекта.
- Объединение вариантов в коллекции. Чтобы была возможность одной кнопкой или одним статик методом собрать сразу некоторое подмножество билдов.
- Упаковка билда в архив. Это опциональное требование. В случае использования плагина в паре с билд сервером этот пункт можно реализовать на его стороне. Но жизнь упростить хочется упростить всем, а билд сервер есть не всегда.
- Перемещение файлов на разных стадиях сборки. Например нужно подменить какой-нибудь ассет (иконку, сплэш, сцену и т.д.) перед сборкой (подмена ассетов при этом должна быть обратима, сборка билда не должна изменять текущее состояние проекта). Или переименовать в готовом виндовом билде {exe_name}_Data в Data (весьма раздражающее поведение юнити, без этой манипуляции нельзя переименовать экзешник). Или опять же в виндовых билдах расставить нужные StreamingAssets. Делать это нужно до упаковки архива. А потом например перетащить все архивы куда-то в одно место.
- У юнити проектов огромное количество настроек и нам совершенно не хочется писать свой UI для них всех. Тем более, что он уже есть в самом юнити. Поэтому настройки проекта будем менять как обычно, а в плагине просто следить за изменениями настроек и сохранять в нужные варианты.
- Хотелось бы хранить каждый вариант в отдельном файле, чтобы была возможность таскать их между проектами.
- Желательно чтобы работало с юнити начиная с версии 5.6. Есть некоторое количество легаси проектов с большим количеством возни вокруг билдов.
Погнали
Основной частью нашего плагина является работа с настройками проекта, а так же хранение и обработка индивидуальных настроек вариантов. Поэтому сконцентрируем внимание именно на этом.
Все настройки юнити хранит в директории ProjectSettings в корне проекта. И первым делом при инициализации мы сохраним эти настройки в директории плагина (к слову это BuildVariants в корне проекта). Это потребуется для того чтобы мы могли отслеживать все последующие изменения. Далее нам нужно их как-то хранить. Самым простым было бы просто копировать ProjectSettings в отдельное место под каждый вариант. И в момент его активации просто подменять их целиком. Идея вполне рабочая и поначалу я рассматривал именно ее. Но это полностью лишает нас возможности наследовать варианты. И если мы уже пишем плагин, то будем сразу делать красиво. Значит нам нужно научиться читать и писать содержимое ProjectSettings.
Для этого нужно понять, что же такое там хранится. А хранятся там самые обыкновенные ассеты, которые сериализуются и десереализуются штатным образом. Это значит, что мы можем прочитать их через AssetsDatabase и получить SerializedObject. Это уже что-то. Потому что мы можем пройтись по всем SerializedProperty, отследить их различия с исходным «снимком» и сохранить их в варианте. Потом при сборке или активации варианта мы собираем изменения по всей цепочке наследования, применяем их к «снимку» и подсовываем в ProjectSettings. Вуаля.
Но у SerializedProperty есть некоторые неприятные особенности: способ хранения значений (все эти intValue, floatValue, isArray), отсутствие нормального механизма их сравнения (есть два родственных объекта, как нам понять все ли SerializedProperty у них имеют одинаковые значения) и парадоксальное (а может и нет) отсутствие аттрибута Serializable (а значит для сохранения нам нужен будет какой-то враппер). Не могу сказать, что это какие-то фатальные недостатки, Все это делалось и не раз в многочисленных editor скриптах. Но хочется все же чего-то более простого, понятного и универсального.
А может быть мы немного ограничим применение нашего плагина? Пусть он работает только на проектах с текстовой сериализацией ассетов (а кто-то еще пользуется бинарной?). Тогда все ProjectSettings будут храниться в виде yaml документов. Можно взять библиотеку YamlDotNet и с ее помощью парсить и сохранять настройки. Заодно и свои конфиги хранить в 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 для свежих версий юнити, надеюсь это позволит сделать все более красиво и удобно. Хочется еще прикрутить версионирование и некое подобие переменных окружения. Может быть общественность подскажет что-нибудь еще по функционалу или юзабилити.
Поэтому буду рад любым пожеланиям, замечаниям, вопросам и баг репортам. Легких билдов вам!
Источник
СМИ: до покупки студии компанией Sony Bungie работала над пятью новыми играми
Вышел трейлер LEGO-спецвыпуска «One Piece. Большой куш», в котором Усопп перепишет историю
Премьера сериала «Фонари» стала самой успешной среди проектов DC Studios на HBO Max
Prime 1 Studio открыла предзаказы на статую по фильму «Годзилла против Кинга Гидоры»
Netflix представил свежий постер новой адаптации One Piece от WIT Studio
Хакеры начали распространять опасные файлы под видом слитого ПК-билда GTA VI
Опубликован трейлер аниме «Бронированные воины Вотомов: Серая ведьма» от Мамору Осии
Актер роли Артура из Red Dead Redemption 2 оценил Kingdom Come: Deliverance II, на что отреагировал исполнитель роли Индржиха