Советы и подводные камни Unity Asset Bundles

ATTILIO CAROTENUTO / UNITYTechnical Lead
Apr 16, 2024|8 Мин
Советы и подводные камни Unity Asset Bundles
Эта веб-страница была переведена с помощью машинного перевода для вашего удобства. Мы не можем гарантировать точность или надежность переведенного контента. Если у вас есть вопросы о точности переведенного контента, обращайтесь к официальной английской версии веб-страницы.

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

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

Хотя существует множество руководств по работе Asset Bundles, я хотел бы рассказать о некоторых менее известных аспектах этой системы, уделяя особое внимание производительности игр, использованию памяти во время выполнения и общей совместимости.

Погрузка и разгрузка активов

Когда вы пытаетесь использовать актив, содержащийся в пакете, Unity убеждается, что соответствующий пакет загружен в память, а затем, в свою очередь, загружает актив в память.

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

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

Совместимость версий двигателя

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

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

Загрузка пакетов из другой версии Unity также связана с производительностью, о чем мы поговорим в разделе TypeTree ниже.

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

Кросс-платформенная совместимость

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

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

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

Загрузка кэша

Кэш загрузки - это общий пул страниц, в котором Unity хранит данные о последних обращениях к вашим пакетам активов. Этот параметр является глобальным, поэтому он будет общим для всех пакетов ассетов в вашей игре.

Это было введено совсем недавно, я полагаю, в Unity 2021.3, а затем перенесено в 2019.4. До этого Unity полагалась на отдельные кэши для каждого Asset Bundle, что приводило к значительному увеличению потребления памяти во время выполнения (об этом ниже, в разделе "Буферы сериализованных файлов").

По умолчанию этот параметр равен 1 МБ, но его можно изменить, задав параметр AssetBundle.memoryBudgetKB.

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

Дополнительные внутренние данные

Вместе с игровыми активами пакеты ассетов содержат кучу дополнительной информации и заголовков, которые используются Unity для определения того, какие активы и как загружать, а также специальный кэш (в зависимости от используемой вами версии Unity).

Оглавление

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

Таблица предварительной нагрузки

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

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

TypeTrees

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

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

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

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

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

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

Сериализованные файловые буферы

Примечание: Начиная с Unity 2019.4 он был заменен глобальным, общим кэшем Loading, как описано выше.

Когда загружается Asset Bundle, Unity выделяет внутренние буферы для хранения в памяти сериализованных файлов.

Обычные пакеты активов содержат один сериализованный файл, в то время как пакеты активов потоковых сцен содержат до двух файлов для каждой сцены, содержащейся в этом пакете. Размер этих буферов зависит от платформы. На Switch, PlayStation и Windows RT он будет равен 128 КБ, в то время как на всех остальных платформах буфер составит 14 КБ.

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

Проверки целостности CRC

CRC (Cyclic Redundancy Check) используется для проверки контрольной суммы ваших пакетов активов, гарантируя, что содержимое, поставляемое в вашу игру, будет именно таким, как вы ожидаете. CRC рассчитывается на основе несжатого содержимого пакета.

На консолях Asset Bundles обычно включаются в установку игры на локальное хранилище или загружаются как DLC, что делает проверку CRC ненужной. На других платформах, таких как ПК или мобильные устройства, важно проводить CRC-проверку пакетов, загруженных с CDN. Это необходимо для того, чтобы файл не был поврежден или усечен, что может привести к сбоям, а также для предотвращения возможного вмешательства.

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

Сокращение накладных расходов на поиск активов

По умолчанию Unity предлагает три способа поиска активов в пакетах:

  • Относительный путь проекта (Assets/Prefabs/Characters/Hero.prefab)
  • Имя файла актива (герой)
  • Имя файла ассета с расширением (Hero.prefab)

Это удобно, но за это приходится платить. Для поддержки двух последних методов Unity необходимо создавать таблицы поиска, которые могут занимать значительный объем памяти для больших пакетов.

Кроме того, загрузка активов с использованием метода, отличного от метода Project Relative Path, приведет к снижению производительности, опять же из-за необходимости поиска в таблице.

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

Для этого вы можете установить эти два флага при создании пакетов:

Чтобы узнать больше об управлении активами, поделиться отзывами или пообщаться с сообществом и сотрудниками Unity, посетите форум "Управление активами".