Unity — платформа, которая существует довольно давно и постоянно развивается. Однако, работая в ней с несколькими проектами одновременно, все еще можно столкнуться со сложностями в использовании общих исходников (.cs), библиотек (.dll) и остальных ассетов (изображения, звуки, модели, префабы). В этой статье мы расскажем о нашем опыте работы с нативным решением такой проблемы для Unity.


Методы распространения общих ресурсов


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

1. Дублирование — «руками» дублируем ресурсы между проектами.

Плюсы:

  • Подходит для всех видов ресурсов.
  • Нет проблем с зависимостями.
  • Нет проблем с GUID’ами ассетов.

Минусы:

  • Гигантские репозитории.
  • Нет возможности версионирования.
  • Сложность отслеживания изменений в общих ресурсах.
  • Сложность обновления общих ресурсов.

2. Git submodules — распространение общих ресурсов через внешние подмодули.

Плюсы:

  • Можно работать с исходниками.
  • Можно распространять ассеты.
  • Нет проблем с зависимостями.

Минусы:

  • Необходим навык работы с Git.
  • Git не очень дружит с бинарными файлами — придется подключать LFS.
  • Разграничение доступа для репозиториев.
  • Сложности при повышении и понижении версии.
  • Возможны коллизии GUID’ов и нет однозначного поведения со стороны Unity для их разрешения.

3. NuGet — распространение общих библиотек через NuGet-пакеты.

Плюсы:

  • Удобная работа с проектами, не зависящими от Unity.
  • Удобное версионирование и разрешение зависимостей.

Минусы:
  • Unity не умеет работать с NuGet-пакетами «из коробки» (на GitHub можно найти NuGet Package Manager for Unity, который исправляет это, но есть нюансы).
  • Сложности при распространении остальных видов ассетов.

4. Unity Package Manager — распространение общих ресурсов через нативное решение для Unity.

Плюсы:

  • Нативный интерфейс для работы с пакетами.
  • Защита от перезаписи .meta файлов в пакетах при конфликтах GUID’ов.
  • Возможность версионирования.
  • Возможность распространения всех видов ресурсов для Unity.

Минусы:

  • Все еще могут случаться конфликты GUID’ов.
  • Нет документации для реализации.

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

Unity Package Manager


Unity Package Manager (далее UPM) — инструмент для управления пакетами. Его добавили в Unity 2018.1, и он использовался только для пакетов, которые разрабатывались Unity Technologies. Однако начиная с версии 2018.3 появилась возможность добавления кастомных пакетов.


Интерфейс Unity Package Manager

Пакеты не попадают в исходники проекта (директорию Assets). Они находятся в отдельной директории %projectFolder%/Library/PackageCache и никак на проект не влияют, их единственное упоминание в исходниках — в файле packages/manifest.json.


Пакеты в файловой системе проекта

Источники пакетов


UPM может использовать несколько источников пакетов:

1. Файловая система.

Плюсы:

  • Скорость реализации.
  • Не требует сторонних инструментов.

Минусы:

  • Сложность версионирования.
  • Необходим общий доступ к файловой системе для всех, кто работает с проектом.

2. Git-репозиторий.

Плюсы:

  • Нужен только Git-репозиторий.

Минусы:

  • Нельзя переключаться между версиями через окно UPM.
  • Работает не со всеми Git-репозиториями.

3. npm-репозиторий.

Плюсы:

  • Полностью поддерживает функционал UPM и используется для распространения официальных пакетов Unity.

Минусы:

  • В настоящее время игнорирует все строковые версии пакетов, кроме «-preview».

Ниже мы рассмотрим реализацию UPM + npm. Эта связка удобна, поскольку позволяет работать с любыми видами ресурсов и управлять версиями пакетов, а также полностью поддерживает нативный интерфейс UPM.

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

Настройка окружения


Для начала нужно установить node.js.

Создание пакета


Чтобы создать пакет, необходимо поместить файл package.json, который будет его описывать, в директорию с содержимым этого пакета. Нужно сделать следующее:

  1. Перейти в директорию проекта, которую хотим сделать пакетом.
  2. Выполнить команду npm init и во время диалога ввести необходимые значения. Для name указываем имя в формате реверс домена, например com.plarium.somepackage.
  3. Для удобного отображения имени пакета — добавить свойство displayName в package.json и заполнить его.
  4. Так как npm js-ориентирован, в файле есть не нужные нам свойства main и scripts, которые Unity не использует. Лучше их удалить, чтобы не засорять описание пакета. Файл должен выглядеть примерно так:

    {
     "name": "com.plarium.somepackage",
     "displayName": "Some Package",
     "version": "1.0.0",
     "description": "Some Package Description",
     "keywords": [
       "Unity",
       "UPM"
     ],
     "author": "AUTHOR",
     "license": "UNLICENSED"
    }

  5. Открыть Unity и сгенерировать .meta файл для package.json (Unity не видит ассеты без .meta файлов, пакеты для Unity открываются только для чтения).

Отправка пакета


Для отправки пакета необходимо выполнить команду: npm publish --registry *адрес до хранилища пакетов*.

Установка и обновление пакетов через Unity Package Manager


Чтобы добавить пакет в Unity-проект, нужно:

  1. Внести в файл manifest.json информацию об источнике пакетов. Для этого необходимо добавить свойство scopedRegistries и указать скоупы и адрес источника, по которому будут искаться конкретные скоупы.

    
    "scopedRegistries": [
       {
         "name": "Main",
         "url": "адрес до хранилища пакетов",
         "scopes": [
           "com.plarium"
         ]
       }
     ]
    
  2. Перейти в Unity и открыть окно Package Manager’а (работа с кастомными пакетами не отличается от работы со встроенными).
  3. Выбрать All Packages.
  4. Найти нужный пакет и добавить его.


Работа с исходниками и отладка


Чтобы исходники подключились к проекту, необходимо создать Assembly Definition для пакета.

Использование пакетов не ограничивает возможности для отладки. Однако при работе с пакетами в Unity нельзя перейти в IDE по клику на ошибку в консоли, если ошибка произошла в пакете. Это связано с тем, что Unity не видит скрипты как отдельные файлы, поскольку при использовании Assembly Definition они собираются в библиотеку и подключаются к проекту. При работе с исходниками из проекта переход в IDE по клику доступен.

Скрипт в проекте с подключенным пакетом:


Скрипт из пакета с работающим брейкпоинтом:


Срочное внесение исправлений в пакеты


Добавленные в проект пакеты Unity открыты только для чтения, но их можно редактировать в кэше пакетов. Для этого необходимо:

  1. Перейти в пакет в кэше пакетов.


  2. Внести необходимые изменения.
  3. Обновить версию в файле package.json.
  4. Отправить пакет npm publish --registry *адрес до хранилища пакетов*.
  5. Обновить версию пакета до исправленной через интерфейс UPM.

Конфликты импорта пакетов


При импорте пакетов могут произойти следующие конфликты GUID’ов:

  1. Пакет — пакет. Если при импорте пакета обнаружится, что в уже добавленных пакетах есть ассеты с таким же GUID’ом, ассеты с совпавшими GUID’ами из импортируемого пакета не добавятся в проект.
  2. Пакет — проект. Если при импорте пакета обнаружится, что в проекте есть ассеты с совпадающими GUID’ами, то ассеты из пакета не добавятся в проект. Однако ассеты, зависящие от них, начнут использовать ассеты из проекта.

Перенос ассетов из проекта в пакет


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

Важно: при копировании ассета из проекта в пакет произойдет конфликт «Пакет — проект», описанный в разделе выше.

Возможные решения конфликтов


  1. Переназначение GUID’ов по собственным алгоритмам при импорте всех ассетов, чтобы исключить коллизии.
  2. Добавление всех ассетов в один проект с их последующим разделением на пакеты.
  3. Создание базы данных, содержащей GUID’ы всех ассетов, и проведение валидации при отправке пакетов.

Заключение


UPM — новое решение для распространения общих ресурсов на Unity, которое может стать достойной альтернативой существующим методам. Рекомендации, описанные в статье, возникли на основе реальных кейсов. Надеемся, они вам пригодятся.

Комментарии (6)


  1. Leopotam
    26.03.2019 18:18

    Единственное что хочется добавить — странное поведение при определении версии, upm не любит ведущие нули, т.е нельзя сделать версию «01.02.03», придется писать «1.2.3». Немного неудобно при использовании дат в качестве версий релизного цикла (вместо «2018.03.22» придется написать «2018.4.22»).


    1. slik
      26.03.2019 18:24
      +2

      semver.org

      MUST NOT contain leading zeroes


      1. Leopotam
        26.03.2019 18:59

        Действительно, до этого как-то не приходилось сталкиваться с таким на старой схеме нумерации.


      1. Leopotam
        26.03.2019 19:40

        Интересно, что даже в 2.0.0-rc.2 такого требования не было, как и в 1.0.0 версии.


  1. playermet
    26.03.2019 18:57

    конфликты GUID
    Или я чего-то не понял, или это вообще не является проблемой ввиду субиоктоскопической вероятности сгенерировать два одинаковых GUID.


    1. Plarium Автор
      27.03.2019 16:11

      Здравствуйте, GUID конфликты на, самом деле, происходят, пусть это и не такое частое событие, но иногда с ним сталкиваешься.
      В рамках одного Unity проекта конфликтов быть не может, так как Unity следит за этим и при необходимости перегенеривает GUID.
      При работе с несколькими крупными проектами и выносе общих наработок в отдельные пакеты/репозитории (например, если общие ассеты были вынесены из разных проектов в пакеты), между пакетами могу произойти конфликты. Также конфликты могут произойти между ассетами из проекта и ассетами из пакетов.
      Даже среди пакетов, которые предоставляются Unity, случаются GUID конфликты, такое иногда встречается в «preview» версиях пакетов. Unity это быстро исправляет, но приходилось сталкиваться и с таким.