~3 мин чтения

Addressables — современная загрузка ассетов

Асинхронная загрузка по адресу, замена Resources.Load и AssetBundles.

В Unity исторически есть несколько способов загрузить ассет в рантайме: Resources.Load, AssetBundles, и Addressables (com.unity.addressables). Последний — рекомендованный современный путь, и в новых проектах остальные стоит избегать.

Что не так с Resources.Load

Resources.Load<Texture>("MyTexture") — простой API: достаёте ассет из папки Resources/ по пути. Звучит удобно, но:

  1. Всё в Resources попадает в билд целиком. Любой ассет в Resources/ Unity упаковывает даже если он не используется на текущей сцене. Резко раздувает размер билда.
  2. Нет асинхронной загрузки из коробки. Resources.LoadAsync есть, но не решает первую проблему.
  3. Нет управления версиями. Не можно обновить пакет ассетов отдельно от билда.

Unity сама в документации пишет: “We recommend not using the Resources folder.”

Идея Addressables

Каждому ассету (или группе) присваивается address — строковый ключ. Загружаете по этому ключу асинхронно. Где физически лежит ассет — в локальном билде, на удалённом CDN, в общем bundle с другими — определяется конфигом, а не кодом. Можно обновить группу ассетов на CDN, и игроки получат новые версии без редеплоя билда.

Веб

Code splitting + dynamic import + CDN. Вы вызываете import('./module-heavy') — браузер подгружает chunk по сети. У вас один билд, тяжёлые модули отделены и грузятся по требованию.

Unity

Addressables.LoadAssetAsync<GameObject>("EnemyBoss") — Unity находит, в каком bundle лежит ассет, загружает (с диска или с CDN), возвращает handle. Используете → освобождаете.

Настройка

  1. Установите пакет com.unity.addressables через Package Manager.
  2. Window → Asset Management → Addressables → Groups — откроется окно с группами.
  3. Выберите ассет в Project, в Inspector поставьте галочку Addressable. По умолчанию address = путь к ассету; смените на короткое имя, например EnemyBoss.
  4. Группа — это контейнер ассетов с общими настройками сборки (local/remote, compression, …).

Загрузка в коде

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class BossSpawner : MonoBehaviour
{
    [SerializeField] private AssetReferenceGameObject bossRef;

    private GameObject _bossInstance;

    public async void SpawnBoss() {
        // Загружаем GameObject prefab асинхронно
        AsyncOperationHandle<GameObject> handle = bossRef.LoadAssetAsync<GameObject>();
        await handle.Task;

        if (handle.Status == AsyncOperationStatus.Succeeded) {
            _bossInstance = Instantiate(handle.Result, transform.position, Quaternion.identity);
        }
    }

    private void OnDestroy() {
        // ВАЖНО: освобождаем загруженный ассет, чтобы он мог быть выгружен
        if (bossRef.IsValid()) {
            bossRef.ReleaseAsset();
        }
    }
}

AssetReference — тип поля для Inspector. Перетягиваете в него Addressable-ассет, и в Inspector получаете dropdown выбора. Это типизированно и удобнее, чем строки.

Можно и по строке, если address известен:

AsyncOperationHandle<Sprite> handle = Addressables.LoadAssetAsync<Sprite>("UI/Icons/Coin");
await handle.Task;
icon.sprite = handle.Result;

InstantiateAsync — сразу спавн

Часто нужно не “загрузить ассет, потом Instantiate”, а сразу создать инстанс. Есть short-cut:

AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync("EnemyBoss", spawnPos, Quaternion.identity);
await handle.Task;
GameObject boss = handle.Result;

// Когда босс убит:
Addressables.ReleaseInstance(boss); // освобождает И ассет, И уничтожает GameObject
Парность Load → Release

Каждому Load (или InstantiateAsync) нужен Release. Не освобождаете — ассет остаётся в памяти на весь жизненный цикл приложения. Это самый частый источник утечек в проектах на Addressables.

Группы и профили

В Addressables Groups вы группируете ассеты по логике — например, “WorldStreaming”, “UI”, “DLC_Level1”. Каждой группе задаются настройки:

  • Build Path / Load Path — где собирается и откуда грузится bundle (локально / по URL).
  • Compression — LZ4 (быстро) / LZMA (компактно) / Uncompressed.
  • Bundle Mode — Pack Together / Pack Separately / Pack Together by Label.

Profiles — это наборы переменных ({LOCAL_BUILD_PATH}, {REMOTE_LOAD_PATH}). Удобно для переключения “Editor / Test / Production” одним кликом.

Remote Content и Content Update

Главный плюс Addressables — обновление контента без обновления билда. Сценарий:

  1. Группа “Levels_DLC1” собирается в bundle с Build Path = удалённый CDN.
  2. При запуске игра обращается к catalog (json с описанием версий bundle’ов).
  3. Если каталог обновился — Unity скачивает новые bundle’ы прозрачно.

Это особенно ценно для:

  • DLC и сезонный контент — не пересобираете весь билд, кладёте новые ассеты на CDN.
  • Hot fixes — починили ассет, не нужно ждать review в App Store.
  • A/B-тесты контента — даёте разным игрокам разные группы ассетов.

Когда Resources всё-таки оправдан

  • Один-два маленьких ассета, нужных всегда — например, Resources/DefaultMaterial.mat.
  • Прототипы — пока проще, чем настраивать Addressables.

В продакшне 99% ассетов — через Addressables, оставшиеся — встроенные в сцены или Direct References.

Профайлинг и анализ

В Window → Asset Management → Addressables → Analyze есть набор правил, которые проверят:

  • Все ли ссылки на ассеты резолвятся?
  • Не дублируются ли одни ассеты в разных bundle’ах?
  • Нет ли неиспользуемых ассетов в группах?

Также: Addressables Profiler показывает в рантайме, какие группы загружены, сколько занимают памяти, у кого reference counter что.

В следующей и последней главе главного раздела — HLSL-шейдеры подробнее.