Unity Term Book
高度な技術

Addressables

Addressables はアドレス(キー)でアセットを非同期ロードする高度なシステムです。DLC や CDN 配信、実行時の自動メモリ管理まで扱い、Resources や生 AssetBundle の限界を超えます。図解とシミュレーターで Handle と Release の正しい流れを学べます。

想像してみてください...

Resources.Load() は倉庫へ自分で行く——正確なパスが必要で、待つ必要があります。AddressablesAmazon 配送: 品名(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")

Check Cache

すでにメモリ上?

Hit: instant
ソースから読み込み

ローカル bundle または CDN ダウンロード

Deserialize

展開してメモリ上にオブジェクトを生成

Callback

handle.Completed → result available

Release()

RefCount-- → 0 → メモリからアンロード

ハンズオン手順

1

Addressables をインストール

Window → Package Manager → "Addressables" を検索 → Install。Window → Asset Management → Addressables → Groups で Groups ウィンドウを開く。

2

アセットを Addressable にする

Project で選択 → Inspector → "Addressable" にチェック → アドレス設定(例: "heroes/knight")。

3

Groups を設定(Local vs Remote)

Addressables Groups ウィンドウ: Create New Group → Build Path(Local または CDN 用 Custom Remote Path)。アセットを Group にドラッグ。

4

コンテンツを Build

Addressables Groups → Build → New Build → Default Build Script。Play や本ビルドの前に Build。

5

正しく 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 にする