Польза Addressables в Unity3D и варианты использования

В Unity3D есть новый инструмент Addressables, для чего он нужен и в чём сложности его использования читайте в этой статье.

Addressables следует использовать в тех случаях когда нужно:

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

-загрузить игровые ресурсы через сеть, при таком подходе можно распространять легковесный исполнительный файл, а все тяжёлые графические и аудио ресурсы загрузятся с сервера

-уменьшить потребление памяти, при обычном подходе, все префабы и связанные с ними ресурсы загружены в память уже в тот момент, когда сцена загружена, Addressables позволяет загружать и выгружать из памяти всё нужное в тот момент, когда в префабе пропала необходимость

-ускорить загрузку уровня, при старте уровня можно показать пользователю низкополигональные модели с текстурами низкого разрешения, а более качественные модели и текстуры загружать уже в тот момент, когда пользователь способен взаимодействовать со сценой, и не заставлять ждать когда всё загрузится полностью

Бонус: Addressables можно использовать, чтобы оценить сколько места занимают ресурсы относящиеся к определённой сцене.

Установка Addressables

Addressables устанавливается в проект при помощи Package Manager. Тремя простыми шагами:

Польза Addressables в Unity3D и варианты использования

Затем надо открыть окно с группами:

Польза Addressables в Unity3D и варианты использования

И создать файл настроек:

Польза Addressables в Unity3D и варианты использования

По умолчанию будет одна группа:

Польза Addressables в Unity3D и варианты использования

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

Польза Addressables в Unity3D и варианты использования

Как контент сделать загружаемым через Addressables.

Можно двумя способами сделать контент:
1) перетянув нужный файл в группу
2) поставив галочку в инспекторе рядом с надписью Addressable

Польза Addressables в Unity3D и варианты использования

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

Польза Addressables в Unity3D и варианты использования

Как использовать контент добавленный в Addressables

Пример кода для загрузки Sprite в Image:

using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.UI; public class AddressablesSpriteLoader : MonoBehaviour { [SerializeField] AssetReference loadableSprite; [SerializeField] Image uiImage; private IEnumerator Start() { AsyncOperationHandle<Sprite> handle = loadableSprite.LoadAssetAsync<Sprite>(); yield return handle; if (handle.Status == AsyncOperationStatus.Succeeded) { Sprite sprite = handle.Result; // Спрайт загружен и готов к использованию uiImage.sprite = sprite; // А здесь ресурс можно уже удалять Addressables.Release(handle); } } }

Или можно иначе, используя async/await:

using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.UI; public class AddressablesSpriteLoader : MonoBehaviour { [SerializeField] AssetReference loadableSprite; [SerializeField] Image uiImage; private async void Start() { AsyncOperationHandle<Sprite> handle = loadableSprite.LoadAssetAsync<Sprite>(); await handle.Task; if (handle.Status == AsyncOperationStatus.Succeeded) { Sprite sprite = handle.Result; // Спрайт загружен и готов к использованию uiImage.sprite = sprite; // А здесь ресурс можно уже удалять Addressables.Release(handle); } } }

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

Пример кода для Prefab'а:

using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public class AddressablesPrefabInstantiator : MonoBehaviour { [SerializeField] AssetReference loadablePrefab; private async void Start() { AsyncOperationHandle<GameObject> handle = loadablePrefab.LoadAssetAsync<GameObject>(); await handle.Task; if (handle.Status == AsyncOperationStatus.Succeeded) { GameObject gameObjectPrefab = handle.Result; Instantiate(gameObjectPrefab); Addressables.Release(handle); } } }

Или сокращённый вариант на случай, если нужно только отобразить объект в сцене:

using UnityEngine; using UnityEngine.AddressableAssets; public class AddressablesPrefabInstantiator : MonoBehaviour { [SerializeField] AssetReference loadablePrefab; private void Start() { loadablePrefab.InstantiateAsync(); } }

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

Польза Addressables в Unity3D и варианты использования

Пример кода для сцены:

using UnityEngine; using UnityEngine.AddressableAssets; public class SceneLoader : MonoBehaviour { [SerializeField] private AssetReference scene; public void LoadScene() { scene.LoadSceneAsync(UnityEngine.SceneManagement.LoadSceneMode.Single); } }

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

И НЕ добавляйте сцены, загружаемые через Addressables, в список "Scenes In Build", иначе эти сцены будут занимать место и в самом билде и в файлах Addressables.

Как собрать исполняемый билд

Если мы соберём билд привычным способом, то получим ошибку:

Exception encountered in operation UnityEngine.ResourceManagement.ResourceManager+CompletedOperation`1[UnityEngine.Sprite], result='', status='Failed': Exception of type 'UnityEngine.AddressableAssets.InvalidKeyException' was thrown., Key=509e2ec2f5450c7418713755b837e796, Type=UnityEngine.Sprite

Потому, что мы не поместили файлы Addressables рядом с исполняемым файлом. Возникает два вопроса:
1) Где взять файлы?
2) Куда и как их положить?

У каждой группы есть настройки откуда её загружать:

Польза Addressables в Unity3D и варианты использования

Build Path - куда скрипт сборки положит файлы

Load Path - откуда эти файлы будет пытаться загрузить процесс

Самый простой вариант - BuildTarget как на скриншоте.

Польза Addressables в Unity3D и варианты использования

После этого можно запускать сборку контента. Файлы будут лежать вот в этой папке:

Польза Addressables в Unity3D и варианты использования

Её же и нужно положить в корень папки билда рядом с exe.

Как загрузить контент через сеть

Для того чтобы файлы с контентом подгружались через сеть, в Addressables есть специальная опция, её можно задействовать открыв Profiles:

Польза Addressables в Unity3D и варианты использования

Задав ссылку на сервер:

Польза Addressables в Unity3D и варианты использования

И установив LoadPath равным RemoteLoadPath:

Польза Addressables в Unity3D и варианты использования

Как сэкономить память

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

Предположим, что всё работает как надо, тогда вот код, который позволит загрузить префаб, инстанциировать его и выгрузить через 5 секунд:

using System.Collections; using UnityEngine; using UnityEngine.AddressableAssets; public class AddressablesPrefabInstantiator : MonoBehaviour { [SerializeField] AssetReference loadablePrefab; public void MakeInstance() { StartCoroutine(MakeInstanceCoroutine()); } private IEnumerator MakeInstanceCoroutine() { var result = Addressables.InstantiateAsync(loadablePrefab); yield return new WaitForSeconds(5); Addressables.ReleaseInstance(result); } }

Важный момент - не удаляйте объекты через Destroy, или после обычного Destroy вызывайте Addressables.ReleaseInstance на объекте AsyncOperationHandle, тогда счётчик ссылок, который помогает Addressables выгружать ненужное, будет правильно понимать сколько ссылок осталось и вовремя выгрузит лишнее из памяти.
Так же инстанциируйте через Addressables.InstantiateAsync, это тоже связано со счётчиком. Загрузка через LoadAsync с последующим инстанциированием "как обычно" приведёт к тому, что Addressables не будет знать что выгружать надо, а что нельзя.

Для того, чтобы выгрузить из памяти сцену загруженную через Addressables надо всего лишь загрузить другую сцену в режиме Single.

53
24 комментария