Addressables
Addressables là hệ thống quản lý asset nâng cao — load bất đồng bộ bằng địa chỉ (key), hỗ trợ DLC, streaming CDN và quản lý bộ nhớ tự động lúc runtime.
Hãy tưởng tượng...
Resources.Load() giống như đi vào kho và lấy đồ trực tiếp — phải biết đường dẫn chính xác và đợi mãi. Addressables như dịch vụ giao hàng Amazon: bạn chỉ cần đặt tên món (address), dịch vụ lo phần còn lại — tìm trong kho local, hoặc tải về từ CDN nếu chưa có. Bạn nhận được handle như biên nhận tracking — không chặn game, tới khi hàng về thì callback. Và khi trả handle = dịch vụ thu hồi bộ nhớ tự động.
Khái niệm chi tiết
Addressable Asset System giải quyết giới hạn của Resources folder (không thể unload riêng lẻ, phải biết path cứng, không hỗ trợ CDN) và AssetBundles (API phức tạp, manual dependency tracking). Addressables xây trên AssetBundles nhưng tự động hoá phần lớn công việc.
Mỗi asset được đánh dấu là Addressable và gán một Address (string key tự
đặt, ví dụ:
“prefabs/hero_sword”). Asset được nhóm vào Groups — mỗi group compile thành một AssetBundle. Group
có thể cấu hình: Local (build trong game) hoặc Remote (upload lên CDN).
Labels cho phép load nhiều asset cùng lúc theo tag.
API chính:
Addressables.LoadAssetAsync<T>(key)
trả về
AsyncOperationHandle<T>
— có thể await trong async method, hoặc subscribe
.Completed
callback. Quan trọng: phải gọi
Addressables.Release(handle)
khi không cần nữa — nếu không, asset không được unload, dẫn đến memory leak.
InstantiateAsync kết hợp load + Instantiate: tự động unload
khi gọi
Addressables.ReleaseInstance(go)
thay vì Destroy thông thường. Addressables cũng hỗ trợ play mode scripts: Fast
Mode (không bake bundle — phát triển nhanh), Virtual Mode (giả lập bundle), và Packed Play Mode
(test chính xác bundle).
Luồng Async Load
Addressables.LoadAssetAsync("heroes/knight")
Đã có trong memory?
Local bundle hoặc CDN download
Giải nén & tạo object trong memory
handle.Completed → result available
RefCount-- → 0 → unload từ memory
Hướng dẫn thực hành
Cài đặt Addressables
Window → Package Manager → tìm "Addressables" → Install. Window → Asset Management → Addressables → Groups để mở Addressables Groups window.
Đánh dấu asset là Addressable
Chọn asset trong Project → Inspector → tick "Addressable" checkbox → đặt tên address (ví dụ: "heroes/knight").
Cấu hình Groups (Local vs Remote)
Addressables Groups window: Create New Group → đặt Build Path (Local hoặc Custom Remote Path cho CDN). Drag asset vào group.
Build content
Addressables Groups → Build → New Build → Default Build Script. Build trước khi play hoặc build game thật.
Load và Release đúng cách
LoadAssetAsync hoặc InstantiateAsync với address. Giữ handle. Khi xong: Release(handle) hoặc ReleaseInstance(go).
Trình mô phỏng tương tác
Mô phỏng luồng async loading — xem Reference Count và Memory Pool.
Nhấn Load để bắt đầu...
Loaded
0
Total Refs
0
Memory ~
0 MB
Ví dụ Code
Cơ bảnLoad prefab bất đồng bộ và Instantiate — dùng 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()
{
// Load async — không block main thread
knightHandle = Addressables.LoadAssetAsync<GameObject>("heroes/knight");
await knightHandle.Task; // Hoặc dùng .Completed callback
if (knightHandle.Status == AsyncOperationStatus.Succeeded)
{
Instantiate(knightHandle.Result, transform.position, Quaternion.identity);
}
}
void OnDestroy()
{
// QUAN TRỌNG: phải Release để tránh memory leak
if (knightHandle.IsValid())
Addressables.Release(knightHandle);
}
}
// Hoặc dùng InstantiateAsync (auto-manage lifecycle)
async void SpawnEnemy(string address, Vector3 pos)
{
var handle = Addressables.InstantiateAsync(address, pos, Quaternion.identity);
await handle.Task;
// Khi enemy chết: Addressables.ReleaseInstance(go) thay vì Destroy
}Ví dụ Code
Nâng caoLoad nhiều asset cùng lúc bằng Label và preload scene tiếp theo trong 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 tất cả asset có label "ui_icons" cùng lúc
async Task LoadUIIcons()
{
var handle = Addressables.LoadAssetsAsync<Sprite>(
"ui_icons",
sprite => { Debug.Log($"Loaded: {sprite.name}"); }
);
await handle.Task;
handles.Add(handle);
}
// Preload scene tiếp theo trong background khi player đang chơi
AsyncOperationHandle<UnityEngine.ResourceManagement.ResourceProviders.SceneInstance> sceneHandle;
public async void PreloadNextScene(string sceneAddress)
{
sceneHandle = Addressables.LoadSceneAsync(sceneAddress,
UnityEngine.SceneManagement.LoadSceneMode.Additive,
activateOnLoad: false); // Load nhưng chưa activate
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);
}
}📌 Ghi nhớ nhanh
- ▸LoadAssetAsync = load không block; trả về Handle
- ▸Release(handle) bắt buộc khi xong — tránh memory leak
- ▸InstantiateAsync + ReleaseInstance thay cho Instantiate/Destroy
- ▸Label = load nhiều asset cùng lúc theo nhóm
- ▸Fast Mode: dùng khi develop (nhanh); Packed Mode: test đúng bundle
⚠️ Lỗi thường gặp
❌ Không Release handle → Memory leak nghiêm trọng
Asset vẫn ở RAM dù không còn dùng; reference count không về 0
✅ Luôn Release trong OnDestroy, luôn giữ handle reference
❌ Exception: Attempting to use an invalid operation handle
Release handle rồi tiếp tục dùng, hoặc double-release
✅ Check handle.IsValid() trước khi Release; null handle sau release