Расширенные вопросы и ответы: Оптимизация памяти и размера сборки с помощью Addressables

В феврале, в рамках своей работы в качестве старшего консультанта по разработке программного обеспечения в компании Unity Accelerate Solutions, я провел технический вебинар о системе Addressables Asset System. Во время живого сеанса я продемонстрировал различные инструменты профилирования, которые можно использовать для оптимизации объема памяти во время выполнения проекта и размера сборки. Вебинар закончился вопросами и ответами, и наша команда получила больше вопросов, чем успела ответить.
Ниже мы продолжим этот заключительный вопрос-ответ, чтобы ответить на большее количество ваших вопросов.
Q: Нужна ли система Addressables для легких игр, таких как казуальные, аркадные или головоломки, если у меня нет проблем с памятью?
A: Может быть, и нет, но стоит помнить, что система Addressables не только улучшает производительность памяти. Возможность выбора времени загрузки контента может улучшить время загрузки. Создание контента в Addressables позволяет делать итеративные сборки, которые не занимают много времени. Например, если вы внесете небольшое изменение в сценарий, вам не придется перестраивать все свои пакеты.
Q: Освобождаются ли загруженные активы при переключении сцены?
A: Потенциально. Загруженные активы из адресных таблиц, которые уже готовы к освобождению, поскольку их счетчик ссылок равен нулю, могут быть выгружены из памяти во время перехода сцены. При неаддитивном переходе со сцены вызовите Resources.UnloadUnusedAssets(). Это дорого обходится процессору, но позволяет частично выгрузить AssetBundles.
Q: Хорошо ли работают вместе объединение объектов и Addressables?
A: Да. Вы можете один раз загрузить свой объект из Addressables, а затем инстанцировать несколько его копий для создания пула. Когда вы закончите работу с пулом, уничтожьте все объекты и освободите AsyncOperationHandle, который использовался для загрузки актива.
Q: Загружаются ли группы и пакеты в память все сразу?
A: Группы адресатов - это понятие, доступное только редактору. Во время выполнения вы имеете дело только с пакетами. Пакеты загружаются в память только тогда, когда они необходимы, и загружается только нужное содержимое.
Пример: У вас есть один пакет с 10 персонажами в нем. Вы просите Addressables загрузить три символа. Будут загружены метаданные пакета и три символа.
Q: Если я хочу освободить актив, нужно ли мне сохранять AsyncOperationHandle или AssetReference?
A: Мы рекомендуем сохранить ручку и использовать ее, поскольку вы несете ответственность за выпуск контента, когда закончите его использовать.
Например, члены нашей команды часто идут по пути handle, чтобы избежать вызова Instantiate/Release непосредственно на AssetReference.
Q: В чем недостатки множества маленьких пучков?
A: В этой документации перечислены несколько недостатков слишком большого количества пакетов.
Q: Когда требуется актив в пакете, какие накладные расходы имеют другие активы в том же пакете? Если это удаленный пакет, он должен быть загружен, но действительно ли не происходит перерасхода памяти из-за неиспользуемых активов в пакете?
A: Правильно, удаленный пакет должен быть полностью загружен, прежде чем вы сможете его использовать.
Незагруженные активы в загруженном пакете активов имеют минимальные накладные расходы во время выполнения. При загрузке активов из пакета необходимо загрузить метаданные пакета. Часть этих метаданных включает в себя оглавление, в котором перечислены все активы в пакете. Больше активов в пакете - больше метаданных.
Вы можете увидеть эти излишки памяти, сделав снимок с помощью Unity Memory Profiler. На вкладке "Вся память" находится список всех объектов "SerializedFile" в памяти, по одному для каждого пакета. Эти объекты являются метаданными ваших пакетов.
Подробнее об этих метаданных можно узнать из нашей документации.
Q: Какие стратегии пакетирования можно использовать при работе с открытым миром, чтобы выгружать отдельные активы, не выгружая наполовину пакет и не полагаясь на Resources.UnloadUnusedAssets(), чтобы очистить его, без необходимости держать каждый актив в своем собственном пакете?
A: Главное помнить, что контент следует объединять, если вы рассчитываете выгрузить его в одно и то же время. Если в вашем игровом мире есть "статичный" контент, например деревья и камни для определенного биома, которые игрок не будет перемещать, такой контент следует объединить. Любой "динамический" контент, например предметы, которые игрок может подбирать, должен быть упакован отдельно.
В этой записи блога и связанном с ней репозитории GitHub рассказывается о разделении пакетов для игры с открытым миром. В нем также есть возможность дедуплицировать пакеты, чтобы уменьшить объем памяти, занимаемый каждым пакетом. Этапы 4 и 5 особенно актуальны для открытых миров.
Q: Когда следует оставлять включенным параметр "AssetBundle CRC"?
A: Рекомендуется включать эту функцию, исключая кэшированные AssetBundles для удаленных групп, и отключать для локальных групп. Проверка нужна только для того, чтобы убедиться, что данные не были повреждены при загрузке. Почти нет причин делать проверку для локальных AssetBundles.
Q: Когда не стоит использовать адресные таблицы из-за проблем с производительностью процессора при загрузке и выгрузке активов?
A: Система Addressables положительно влияет на производительность загрузки процессора, поскольку не нужно загружать все содержимое заранее.
Если не использовать Addressables при загрузке сцены, то придется загружать все содержимое и ссылки. Если вы переместите содержимое в Addressables, вы сможете выбирать, когда загружать то или иное содержимое.
Например, в сцене есть менеджер инвентаря, который ссылается на 1 000 единиц инвентаря. Если вы не используете Addressables, вам придется загружать каждый меш, текстуру, аудиоклип и т. д. для всех этих предметов инвентаря. Если вы подождете с загрузкой этого содержимого, загрузка сцены будет происходить быстрее.
Q: Должны ли все зависимости адресуемого актива также быть адресуемыми или это необходимо только в том случае, если они являются общими?
A: Зависимости не обязательно должны быть помечены как адресуемые. При необходимости зависимости будут подтягиваться в Addressables в процессе сборки.
Например, если вы сделаете префаб игрока адресуемым, вам не придется вручную помечать сетку, текстуры или звук игрока как адресуемые. Когда пакет будет собран, все зависимости, которые еще не существуют в Addressables, будут автоматически включены в пакет префабов игрока.
Q: Если я забыл выпустить актив и сменил сцену, что произойдет с этим активом?
A: Смена сцен не влечет за собой плохого взаимодействия с ручками. Но если вы загрузите актив и забудете отпустить его хэндл, актив сохранится в памяти.
Addressables имеет внутреннюю систему подсчета ссылок. Ручки - это то, как мы взаимодействуем с этой системой. Загрузка актива увеличивает счетчик ссылок, а освобождение уменьшает его.
Создатели несут ответственность за поддержание этого количества ссылок в актуальном состоянии. Актив будет находиться в памяти до тех пор, пока количество ссылок больше единицы.
Q: В связи с примером из вебинара предположим, что я создаю игру с открытым миром. Босс находится где-то в открытом мире. Когда игрок направляется к боссу, как мне использовать адресные таблицы? Посылать ли команду на загрузку меча async, через триггер, на определенном расстоянии от врага, или как-то иначе?
A: Выбор момента загрузки и выгрузки контента может быть очень тонким. Вы хотите быть уверены, что босс будет готов к тому моменту, когда игрок должен его увидеть, но, возможно, не захотите загружать его слишком рано, когда игрок еще может развернуться и избежать босса.
Хорошо то, что вы можете итеративно выбирать время загрузки и выгрузки контента - вам не обязательно оптимизировать его с первой попытки.
Для начала мы предлагаем загружать все содержимое определенной "зоны", когда игрок приближается к ней (например, игрок подходит к входу в подземелье, что приводит к загрузке всего содержимого внутри подземелья). Если это приводит к излишней нагрузке на память, вы можете добавить более тонкую загрузку и выгрузку.
Если меч загружается недостаточно быстро, подумайте о том, чтобы переместить триггер загрузки на более раннее время, улучшить время загрузки активов меча с помощью модуля CPU Unity Profiler, чтобы увидеть, что загружается, или использовать Addressables синхронно, чтобы убедиться, что загрузка завершена.
Эта документация содержит более подробную информацию и фрагмент кода для синхронных Addressables.
Q: Если я загружаю адресуемый объект при запуске сцены, нужно ли мне иметь экран загрузки для него?
A: Загрузка из адресных таблиц обычно выполняется асинхронно, например с помощью Addressables.LoadAssetAsync().
Возможно, вы не захотите загружать некоторые материалы до выхода из экрана загрузки. Вы можете собрать эти AsyncOperationHandles и подождать, пока необходимые завершатся, прежде чем уйти.
Q: Какой объем памяти занимают адресные метаданные во время выполнения (до загрузки каких-либо данных)?
A: Во время инициализации Addressables загружается файл каталога, чтобы Addressables знала, как сопоставить метки и адреса с активами на диске или в удаленных местах. Больший каталог означает большие затраты памяти во время выполнения.
Размер каталога можно уменьшить, удалив ненужные данные, например, не включая метки или GUID в группы, которым они не нужны, или уменьшив размер существующих данных. Например, установив для группы режим именования внутренних активов на GUID вместо имени файла или полного пути (которые могут быть длиннее). Вы можете просмотреть объем памяти каталога во время выполнения в Unity Memory Profiler.
Q: Что делает редактор Unity за то время, которое он тратит на создание адресных файлов?
A: Журнал отчетов о сборке выводится в папке /Library. Этот журнал показывает каждый шаг процесса сборки. Чтобы добавить в журнал дополнительные сведения, следуйте по этому пути и выберите "Use Detailed Build Log": Включите Edit > Preferences > Scriptable Build Pipeline > Use Detailed Build Log.
Ознакомьтесь с наглядными примерами и документацией по просмотру журнала.
Q: У Resources.Load() также есть проблема дублирования?
A: Да. Полезно рассматривать адресный контент и контент ресурсов как разные "миры". Если у вас есть текстура в /Resources, одна копия этой текстуры будет включена в файл Resources. Если пакеты в Addressables зависят от этой текстуры, каждый пакет включает ее неявную копию. В итоге вы получаете несколько копий текстуры на диске и, возможно, несколько копий в памяти.
Чтобы избежать дублирования, переместите текстуру из /Resources и добавьте ее в группу Addressables.
Q: Возникают ли у вас подобные проблемы с размером на диске, которые решаются удалением дубликатов пакетов, когда вы не используете Addressables?
A: Да. На вебинаре и слайдах мы показываем, как дедупликация двух сцен водных гонок значительно сократила размер сборки.
Q: Как предотвратить дублирование вариантов шейдеров?
A: Шейдеры можно дедуплицировать так же, как и любой другой актив, - явно объявив их в группе.
Если актив явно объявлен в группе Addressables, которая входит в вашу сборку, этот актив не будет дублироваться в нескольких пакетах.
Что касается шейдеров, то обычно в проектах используется группа "Shared shaders", содержащая шейдеры, которые, как ожидается, будут находиться в памяти в течение всего времени работы приложения и будут использоваться совместно с многими активами.
Q: Дублируют ли размер сборки две сцены Unity, использующие один и тот же префаб?
A: Это зависит от того, был ли префаб, от которого зависят сцены, явно включен в Addressables, а также от того, находятся ли сцены в одном или разных пакетах.
Визуальное объяснение того, как происходит дублирование, приведено на слайдах вебинара и в этой записи в блоге в разделе "Этап 4".
Главное помнить, что все содержимое пакета должно иметь доступ ко всем его зависимостям. Если вы помещаете сцену в бандл, все ее зависимости должны быть либо такими, либо такими:
- Явно включено в Адресные таблицы
- Неявно включены в один пакет
Q: Можно ли сравнивать дубликаты в заданных группах, чтобы избежать объединения всех игровых активов в изолированную группу?
A: Да. Вы можете запустить встроенное правило дедупликации, а затем отсортировать активы в окне Addressables Groups по более удобным группам.
Или, более масштабируемый подход - написать свои собственные правила анализа Addressables AnalyzeRules, которые будут появляться в окне Analyze. Встроенные правила поставляются в виде C# в пакете Addressables и могут служить в качестве базового уровня.
Например, вы можете захотеть найти все дубликаты во всех группах, которые начинаются с "Character-". Любые неявные дубликаты можно поместить в группу "Общий персонаж".
Q: Будете ли вы рассказывать об удаленных сборках и локальных путях?
A: На вебинаре мы не рассматривали удаленные и локальные пути, которые называются "профилями адресации". Однако в этой документации мы описываем, что такое профили Addressables и как их использовать.
Q: Как Addressables работает с облачной доставкой контента (CCD)?
A: Интеграция ПЗС рассматривается в этой документации.
Q: Подскажите, пожалуйста, как лучше реализовать вариации Addressables с низким и высоким разрешением?
A: Пример можно найти в образце Addressables Sample на GitHub.
Q: Что делать, если содержимое пакета зашифровано? Расшифровывает ли UnityDataTool содержимое?
A: Нет. Перед тем как UnityDataTool сможет проанализировать содержимое, данные нужно будет расшифровать.
Q: Поддерживается ли сборка пакетов из одного проекта Unity и загрузка пакетов во время выполнения из приложения, созданного из другого проекта?
A: Да. Это можно сделать, используя несколько каталогов одновременно.
Q: Есть ли недостатки в использовании InstantiateAsync, или ситуации, когда лучше использовать LoadAsync + ручной Instantiate?
A: Рекомендуется использовать Addressables.LoadAssetAsync() и вызывать Object.Instantiate(). Addressables.InstantiateAsync() требует больших затрат производительности.
Q: У меня много объектов ScriptableObjects, в которых по крайней мере 1-2 спрайта упоминаются как переменные. Если я хочу изменить спрайты на Addressables, нужно ли мне менять ссылки на Addressables по одной, или есть какой-то трюк, чтобы сделать это?
A: Для преобразования этих ссылок лучше всего использовать скрипт редактора.
Вы можете добавить поля AssetReference в объект ScriptableObject (и временно оставить поля Sprite). Затем вы можете написать сценарий редактора, который будет итеративно просматривать ваши ScriptableObjects, искать актив Sprite в Addressables, чтобы найти связанный AddressableAssetEntry, и сохранять адрес или создавать AssetReference для хранения в ScriptableObject.
Наконец, вы можете удалить прямые ссылки на спрайт и поменять весь связанный с ним код на использование AssetReference.
Q: Можно ли использовать адресные таблицы в играх на WebGL? Если да, есть ли какие-то особенности, на которые следует обратить внимание?
A: Да, и да. Следует отметить два момента: Во-первых, WebGL не поддерживает работу с потоками, поэтому не используйте Tasks. Во-вторых, кэширование в WebGL работает по-другому - мы уже сталкивались с проблемами при кэшировании удаленных AssetBundles.
Q: Если я использую Shader.Find("ShaderName"), это происходит из сборки или адресных файлов?
A: Эти данные поступают из сборки Unity Player, а не из Addressables. Shader.Find() не возвращает результаты из AssetBundles.
Q: Как организовать окно Addressables Groups, если у меня много групп с одинаковыми названиями?
A: Для организации интерфейса Addressables Groups UI можно включить иерархию групп с помощью тире. Это позволит объединить группы с одинаковыми названиями. Например, "Персонаж-персонаж" и "Персонаж-персонаж2" появятся в пользовательском интерфейсе под группировкой "Персонаж".
Это не влияет на способ создания пакетов. Это всего лишь организационное изменение пользовательского интерфейса.
Поделитесь с нами своими отзывами на форуме Addressables. Следите за новыми техническими блогами от других разработчиков Unity в рамках продолжающейся серии Tech from the Trenches.
