~4 min read

Addressables — Modern Asset Loading

Asynchronous loading by address, a replacement for Resources.Load and AssetBundles.

Historically, Unity has had several ways to load an asset at runtime: Resources.Load, AssetBundles, and Addressables (com.unity.addressables). The latter is the recommended modern path, and in new projects you should avoid the others.

What’s wrong with Resources.Load

Resources.Load<Texture>("MyTexture") is a simple API: you fetch an asset from the Resources/ folder by path. It sounds convenient, but:

  1. Everything in Resources goes into the build in full. Unity packs any asset in Resources/ even if it isn’t used in the current scene. This drastically bloats the build size.
  2. No asynchronous loading out of the box. Resources.LoadAsync exists, but it doesn’t solve the first problem.
  3. No version management. You can’t update a pack of assets separately from the build.

Unity itself states in the documentation: “We recommend not using the Resources folder.”

The idea behind Addressables

Each asset (or group) is assigned an address — a string key. You load by this key asynchronously. Where the asset physically lives — in the local build, on a remote CDN, in a shared bundle with others — is determined by configuration, not by code. You can update a group of assets on the CDN, and players will get the new versions without redeploying the build.

Web

Code splitting + dynamic import + CDN. You call import('./module-heavy') — the browser pulls the chunk over the network. You have one build, with heavy modules split out and loaded on demand.

Unity

Addressables.LoadAssetAsync<GameObject>("EnemyBoss") — Unity finds which bundle the asset is in, loads it (from disk or from a CDN), and returns a handle. You use it → you release it.

Setup

  1. Install the com.unity.addressables package via the Package Manager.
  2. Window → Asset Management → Addressables → Groups — the Groups window opens.
  3. Select an asset in the Project and tick the Addressable checkbox in the Inspector. By default the address = the asset’s path; change it to a short name, for example EnemyBoss.
  4. A group is a container of assets with shared build settings (local/remote, compression, …).

Loading in code

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

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

    private GameObject _bossInstance;

    public async void SpawnBoss() {
        // Load the GameObject prefab asynchronously
        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() {
        // IMPORTANT: release the loaded asset so it can be unloaded
        if (bossRef.IsValid()) {
            bossRef.ReleaseAsset();
        }
    }
}

AssetReference is the field type for the Inspector. You drag an Addressable asset into it and get a selection dropdown in the Inspector. It’s typed and more convenient than strings.

You can also use a string if the address is known:

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

InstantiateAsync — spawn immediately

Often you don’t want to “load an asset, then Instantiate”, but to create an instance right away. There’s a short-cut:

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

// When the boss is killed:
Addressables.ReleaseInstance(boss); // releases BOTH the asset AND destroys the GameObject
Pairing Load → Release

Every Load (or InstantiateAsync) needs a Release. If you don’t release, the asset stays in memory for the entire lifetime of the application. This is the most common source of leaks in Addressables projects.

Groups and profiles

In Addressables Groups you group assets by logic — for example, “WorldStreaming”, “UI”, “DLC_Level1”. Each group is given settings:

  • Build Path / Load Path — where the bundle is built and loaded from (locally / by URL).
  • Compression — LZ4 (fast) / LZMA (compact) / Uncompressed.
  • Bundle Mode — Pack Together / Pack Separately / Pack Together by Label.

Profiles are sets of variables ({LOCAL_BUILD_PATH}, {REMOTE_LOAD_PATH}). Convenient for switching “Editor / Test / Production” in a single click.

Remote Content and Content Update

The main upside of Addressables is updating content without updating the build. The scenario:

  1. The “Levels_DLC1” group is built into a bundle with Build Path = a remote CDN.
  2. On launch the game queries the catalog (a JSON describing the bundle versions).
  3. If the catalog has been updated, Unity downloads the new bundles transparently.

This is especially valuable for:

  • DLC and seasonal content — you don’t rebuild the whole build, you just put new assets on the CDN.
  • Hot fixes — you fixed an asset, no need to wait for App Store review.
  • Content A/B tests — you give different players different groups of assets.

When Resources is justified after all

  • One or two small assets that are always needed — for example, Resources/DefaultMaterial.mat.
  • Prototypes — while it’s still simpler than configuring Addressables.

In production, 99% of assets go through Addressables; the rest are embedded in scenes or Direct References.

Profiling and analysis

Under Window → Asset Management → Addressables → Analyze there’s a set of rules that check:

  • Do all asset references resolve?
  • Are the same assets duplicated across different bundles?
  • Are there any unused assets in the groups?

Also: the Addressables Profiler shows at runtime which groups are loaded, how much memory they occupy, and what each one’s reference counter is.

In the next and final chapter of the main section — HLSL shaders in more detail.