Addressables
Addressables is an advanced asset system — load assets asynchronously by address (key), with DLC, CDN streaming, and automatic memory management at runtime.
Imagine...
Resources.Load() is walking into a warehouse yourself — you need the exact path and you wait. Addressables is Amazon delivery: you name the item (address), the service finds it locally or downloads it from a CDN. You get a handle like a tracking number — the game is not blocked; a callback fires when it arrives. Releasing the handle is the service reclaiming memory.
The concept in detail
The Addressable Asset System fixes Resources folder limits (cannot unload one asset, hardcoded paths, no CDN) and raw AssetBundles (complex API, manual dependency tracking). Addressables sits on AssetBundles but automates most of the work.
Each asset is marked Addressable and given an Address (a string
key, e.g.
“prefabs/hero_sword”). Assets go into Groups — each group compiles to one AssetBundle. A group
can be Local (in the build) or Remote (uploaded to a CDN).
Labels let you load many assets at once by tag.
Main API:
Addressables.LoadAssetAsync<T>(key)
returns
AsyncOperationHandle<T>
— await it, or subscribe to
.Completed.
Important: call
Addressables.Release(handle)
when you are done — otherwise the asset never unloads (memory leak).
InstantiateAsync combines load + Instantiate: unload
with
Addressables.ReleaseInstance(go)
instead of Destroy. Addressables also has play mode scripts: Fast Mode (no
bundle bake — fast iteration), Virtual Mode (simulated bundles), and Packed Play Mode (real
bundles).
Async load flow
Addressables.LoadAssetAsync("heroes/knight")
Already in memory?
Local bundle or CDN download
Decompress & create the object in memory
handle.Completed → result available
RefCount-- → 0 → unload from memory
Hands-on steps
Install Addressables
Window → Package Manager → search "Addressables" → Install. Window → Asset Management → Addressables → Groups to open the Groups window.
Mark an asset Addressable
Select it in the Project → Inspector → tick "Addressable" → set the address (e.g. "heroes/knight").
Configure Groups (Local vs Remote)
Addressables Groups window: Create New Group → set Build Path (Local or a Custom Remote Path for CDN). Drag assets into the group.
Build content
Addressables Groups → Build → New Build → Default Build Script. Build before Play or a real game build.
Load and Release correctly
LoadAssetAsync or InstantiateAsync with the address. Keep the handle. When done: Release(handle) or ReleaseInstance(go).
Interactive simulator
Simulate the async load flow — watch Reference Count and the Memory Pool.
Press Load to begin...
Loaded
0
Total Refs
0
Memory ~
0 MB
Code example
BasicLoad a prefab asynchronously and Instantiate — using await (C# async/await).
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using System.Threading.Tasks;
public class AssetLoader : MonoBehaviour
{
AsyncOperationHandle<GameObject> knightHandle;
async void Start()
{
// Async load — does not block the main thread
knightHandle = Addressables.LoadAssetAsync<GameObject>("heroes/knight");
await knightHandle.Task; // Or .Completed callback
if (knightHandle.Status == AsyncOperationStatus.Succeeded)
{
Instantiate(knightHandle.Result, transform.position, Quaternion.identity);
}
}
void OnDestroy()
{
// REQUIRED: Release to avoid a memory leak
if (knightHandle.IsValid())
Addressables.Release(knightHandle);
}
}
// Or InstantiateAsync (auto-managed lifecycle)
async void SpawnEnemy(string address, Vector3 pos)
{
var handle = Addressables.InstantiateAsync(address, pos, Quaternion.identity);
await handle.Task;
// When the enemy dies: Addressables.ReleaseInstance(go) instead of Destroy
}Code example
AdvancedLoad many assets at once by Label and preload the next scene in the background.
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using System.Collections.Generic;
using System.Threading.Tasks;
public class AssetManager : MonoBehaviour
{
readonly List<AsyncOperationHandle> handles = new();
// Load every asset labeled "ui_icons" at once
async Task LoadUIIcons()
{
var handle = Addressables.LoadAssetsAsync<Sprite>(
"ui_icons",
sprite => { Debug.Log($"Loaded: {sprite.name}"); }
);
await handle.Task;
handles.Add(handle);
}
// Preload the next scene in the background while the player is in-game
AsyncOperationHandle<UnityEngine.ResourceManagement.ResourceProviders.SceneInstance> sceneHandle;
public async void PreloadNextScene(string sceneAddress)
{
sceneHandle = Addressables.LoadSceneAsync(sceneAddress,
UnityEngine.SceneManagement.LoadSceneMode.Additive,
activateOnLoad: false); // Load but do not activate yet
await sceneHandle.Task;
Debug.Log("Scene preloaded, waiting for activation");
}
public async void ActivatePreloadedScene()
{
await sceneHandle.Result.ActivateAsync();
}
void OnDestroy()
{
foreach (var h in handles)
Addressables.Release(h);
}
}📌 Quick recap
- ▸LoadAssetAsync = non-blocking load; returns a Handle
- ▸Release(handle) is required when done — avoid leaks
- ▸InstantiateAsync + ReleaseInstance instead of Instantiate/Destroy
- ▸Label = load many assets at once by group
- ▸Fast Mode: development (fast); Packed Mode: real bundle test
⚠️ Common mistakes
❌ Never Release the handle → serious memory leak
The asset stays in RAM; reference count never hits 0
✅ Always Release in OnDestroy, always keep the handle
❌ Exception: Attempting to use an invalid operation handle
Using a handle after Release, or double-release
✅ Check handle.IsValid() before Release; null the handle after