Addressables
Addressables 是 Unity 的高级资源管理系统,按地址(key)异步加载 Asset,支持 DLC、CDN 流式分发,并在运行时自动管理内存引用计数。适合需要热更新、分包下载或大世界流式加载的团队,能显著减少 Resources.Load 硬加载带来的包体膨胀、依赖混乱与内存峰值压力。
想象一下...
Resources.Load() 像亲自走进仓库——必须知道精确路径,还得干等。Addressables 是快递上门:你报品名(address),服务在本地找或从 CDN 下载。你拿到像运单号一样的 handle——游戏不阻塞;到达时回调触发。释放 handle 就是让服务回收内存。
概念详解
Addressable Asset System 解决 Resources 文件夹的局限 (无法单独卸载、路径硬编码、无 CDN)以及原始 AssetBundle 的痛点(API 复杂、依赖需手管)。 Addressables 建立在 AssetBundle 之上,但自动化大部分工作。
每个资源标记为 Addressable 并赋予 Address(字符串 key,例如
“prefabs/hero_sword”)。资源进入 Groups——每组编译成一个 AssetBundle。组可以是
Local(进包)或 Remote(上传 CDN)。
Labels 让你按标签一次加载多个资源。
主要 API:
Addressables.LoadAssetAsync<T>(key)
返回
AsyncOperationHandle<T>
——可 await,或订阅
.Completed。
重要:用完后调用
Addressables.Release(handle)
——否则资源永不卸载(内存泄漏)。
InstantiateAsync 合并加载 + Instantiate:卸载用
Addressables.ReleaseInstance(go)
而非 Destroy。Addressables 还有 play mode scripts:Fast Mode
(不烘 bundle——迭代快)、Virtual Mode(模拟 bundle)、Packed Play Mode(真实 bundle)。
异步加载流程
Addressables.LoadAssetAsync("heroes/knight")
已经在内存中?
本地 bundle 或 CDN 下载
解压并在内存中创建对象
handle.Completed → result available
RefCount-- → 0 → 从内存卸载
动手步骤
安装 Addressables
Window → Package Manager → 搜索 "Addressables" → Install。Window → Asset Management → Addressables → Groups 打开 Groups 窗口。
将资源标为 Addressable
在 Project 中选中 → Inspector → 勾选 "Addressable" → 设置 address(例如 "heroes/knight")。
配置 Groups(Local 与 Remote)
Addressables Groups 窗口:Create New Group → 设置 Build Path(Local 或 CDN 的 Custom Remote Path)。把资源拖进组。
构建内容
Addressables Groups → Build → New Build → Default Build Script。Play 或正式出包前先 Build。
正确 Load 与 Release
用 address 调用 LoadAssetAsync 或 InstantiateAsync。保留 handle。用完:Release(handle) 或 ReleaseInstance(go)。
交互模拟器
模拟异步加载流程——观察 Reference Count 与 Memory Pool。
按下 Load 开始...
Loaded
0
Total Refs
0
Memory ~
0 MB
代码示例
基础异步加载 prefab 并 Instantiate——使用 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
}代码示例
进阶按 Label 一次加载多个资源,并在后台预加载下一场景。
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);
}
}📌 快速记忆
- ▸LoadAssetAsync = 非阻塞加载;返回 Handle
- ▸用完必须 Release(handle)——避免泄漏
- ▸InstantiateAsync + ReleaseInstance 替代 Instantiate/Destroy
- ▸Label = 按组一次加载多个资源
- ▸Fast Mode:开发(快);Packed Mode:真实 bundle 测试
⚠️ 常见错误
❌ 从不 Release handle → 严重内存泄漏
资源留在 RAM;引用计数永不归 0
✅ 始终在 OnDestroy 中 Release,并始终保留 handle
❌ Exception: Attempting to use an invalid operation handle
Release 后再用 handle,或双重释放
✅ Release 前检查 handle.IsValid();之后将 handle 置空