Addressables
Addressables はアドレス(キー)でアセットを非同期ロードする高度なシステムです。DLC や CDN 配信、実行時の自動メモリ管理まで扱い、Resources や生 AssetBundle の限界を超えます。図解とシミュレーターで Handle と Release の正しい流れを学べます。
想像してみてください...
Resources.Load() は倉庫へ自分で行く——正確なパスが必要で、待つ必要があります。Addressables は Amazon 配送: 品名(address)を伝えれば、サービスがローカルか CDN から探します。受け取るのは追跡番号のような handle——ゲームはブロックされず、到着時にコールバックが発火します。handle を Release すると、サービスがメモリを回収します。
概念の詳細
Addressable Asset System は Resources フォルダの限界(単一アセットをアンロードできない、パス固定、CDN 不可)と生 AssetBundle(複雑な API、依存関係の手動追跡)を解消します。Addressables は AssetBundle の上に乗り、作業の大半を自動化します。
各アセットを Addressable にし、Address(文字列キー、例:
“prefabs/hero_sword”)を付けます。アセットは Groups に入り——各 Group が 1 つの AssetBundle にコンパイルされます。Group は Local(ビルド内)または Remote(CDN アップロード)にできます。
Labels でタグ単位の一括ロードが可能です。
主な API:
Addressables.LoadAssetAsync<T>(key)
は
AsyncOperationHandle<T>
を返します——await するか
.Completed
を購読。重要: 使い終わったら
Addressables.Release(handle)
を呼ぶ——呼ばないとアセットはアンロードされません(メモリリーク)。
InstantiateAsync はロード+Instantiate を一体化: 破棄は Destroy ではなく
Addressables.ReleaseInstance(go)
。Addressables には play mode scripts もあります: Fast Mode(バンドル Bake なし——高速反復)、Virtual Mode(シミュレート)、Packed Play Mode(実バンドル)。
非同期ロードの流れ
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" にチェック → アドレス設定(例: "heroes/knight")。
Groups を設定(Local vs Remote)
Addressables Groups ウィンドウ: Create New Group → Build Path(Local または CDN 用 Custom Remote Path)。アセットを Group にドラッグ。
コンテンツを Build
Addressables Groups → Build → New Build → Default Build Script。Play や本ビルドの前に Build。
正しく Load と Release
アドレスで 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: 実バンドル検証
⚠️ よくあるミス
❌ handle を Release しない → 深刻なメモリリーク
アセットが RAM に残り、参照カウントが 0 にならない
✅ OnDestroy で必ず Release、常に handle を保持
❌ Exception: Attempting to use an invalid operation handle
Release 後の handle 使用、または二重 Release
✅ Release 前に handle.IsValid() を確認し、その後 null にする