Addressable アセットのロード
Addressables クラスには、Addressable アセットをロードするメソッドがいくつか用意されています。アセットは、一度に 1 つずつロードすることも、複数を一括でロードすることもできます。ロードするアセットを特定するには、単一のキーまたはキーのリストをロード関数に渡します。キーは、以下のオブジェクトのいずれかです。
- Address: アセットに割り当てたアドレスを含む文字列
- Label: 1 つ以上のアセットに割り当てられたラベルを含む文字列
- AssetReference オブジェクト: AssetReference のインスタンス
- IResourceLocation インスタンス: アセットとその依存関係をロードするための情報を含む中間オブジェクト
アセットロード関数のいずれかを呼び出すと、Addressables システムによって、以下のタスクを実行する非同期操作が開始されます。
- 指定したキー (IResourceLocation キーを除く) のリソースの場所を検索します
- 依存関係のリストを収集します
- 必要なすべてのリモート AssetBundle をダウンロードします
- AssetBundle をメモリ内にロードします
- 操作の Result オブジェクトを、ロードされたオブジェクトに設定します
- 操作の Status を更新し、すべての Completed イベントリスナーを呼び出します
ロード操作が正常に完了すると、Status が Succeeded (成功) に設定され、ロードされたオブジェクトに Result オブジェクトからアクセスできるようになります。
エラーが発生した場合は、操作オブジェクトの OperationException メンバーに例外がコピーされ、Status が Failed (失敗) に設定されます。デフォルトでは、例外が操作の一部としてスローされることはありません。ただし、ハンドラー関数を ResourceManager.ExceptionHandler プロパティに割り当てて、すべての例外を処理することができます。また、Addressable システム設定で Log Runtime Exceptions オプションを有効にすると、Unity [コンソール] にエラーを記録できます。
複数の Addressable アセットをロードできるロード関数を呼び出すときは、いずれかのロード操作が失敗した場合に操作全体を中止するか、それともロードできるアセットをすべてロードするかを指定できます。どちらの場合も、操作ステータスは失敗に設定されます (一部でも失敗した場合に操作全体を中止するには、ロード関数の呼び出しで releaseDependenciesOnFailure
パラメーターを true に設定します)。
非同期操作と、Unity スクリプトでの非同期コードの記述の詳細については、[操作] を参照してください。
1 つのアセットのロード
1 つの Addressable アセットをロードするには、LoadAssetAsync メソッドを使用します。このとき、通常はアドレスをキーとして指定します。
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadAddress : MonoBehaviour
{
public string key;
AsyncOperationHandle<GameObject> opHandle;
public IEnumerator Start()
{
opHandle = Addressables.LoadAssetAsync<GameObject>(key);
yield return opHandle;
if (opHandle.Status == AsyncOperationStatus.Succeeded)
{
GameObject obj = opHandle.Result;
Instantiate(obj, transform);
}
}
void OnDestroy()
{
Addressables.Release(opHandle);
}
}
Note
LoadAssetAsync を呼び出すときは、アドレスだけでなく、ラベルやその他のタイプのキーを使用できます。ただし、キーが複数のアセットに解決される場合は、最初に見つかったアセットだけがロードされます。例えば、複数のアセットに適用されているラベルを指定してこのメソッドを呼び出すと、それらのアセットのうち、最初に見つかったものが Addressables から返されます。
複数のアセットのロード
複数の Addressable アセットを 1 回の操作でロードするには、LoadAssetsAsync メソッドを使用します。この関数を使用するときは、ラベルなどの単一のキー、またはキーのリストを指定できます。
複数のキーを指定するときは、[MergeMode] を指定して、各キーに一致するアセットのセットをどのように結合するかを決定できます。
- **Union **: いずれかのキーに一致するアセットを選択します
- Intersection: すべてのキーに一致するアセットを選択します
- UseFirst: 有効な場所に解決される最初のキーに一致するアセットのみを選択します
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadMultiple : MonoBehaviour
{
// Label strings to load
public List<string> keys = new List<string>() {"characters", "animals"};
// Operation handle used to load and release assets
AsyncOperationHandle<IList<GameObject>> loadHandle;
// Load Addressables by Label
public IEnumerator Start()
{
float x = 0, z = 0;
loadHandle = Addressables.LoadAssetsAsync<GameObject>(
keys,
addressable =>
{
//Gets called for every loaded asset
Instantiate<GameObject>(addressable,
new Vector3(x++ * 2.0f, 0, z * 2.0f),
Quaternion.identity,
transform);
if (x > 9)
{
x = 0;
z++;
}
}, Addressables.MergeMode.Union, // How to combine multiple labels
false); // Whether to fail and release if any asset fails to load
yield return loadHandle;
}
private void OnDestroy()
{
Addressables.Release(loadHandle);
// Release all the loaded assets associated with loadHandle
// Note that if you do not make loaded addressables a child of this object,
// then you will need to devise another way of releasing the handle when
// all the individual addressables are destroyed.
}
}
releaseDependenciesOnFailure
パラメーターで、どのようにロードエラーを処理するかを指定できます。true に設定すると、1 つのアセットのロードでエラーが発生した場合に、操作が失敗します。操作と、正常にロードされていたアセットはすべて解放されます。
false に設定すると、操作でロードできるオブジェクトはすべてロードされ、操作は解放されません。失敗した場合でも、Failed (失敗) というステータスで操作が完了します。さらに、返されるアセットのリストには、失敗したアセットの位置に null 値が含まれます。
使用するためにまとめてロードする必要のあるアセットのグループをロードするときは、releaseDependenciesOnFailure
を true に設定します。例えば、ゲームレベル用のアセットをロードする場合は、必要なアセットの一部のみがロードされるよりも、操作全体が失敗した方が合理的である可能性があります。
ロードしたアセットとそのキーの相関
1 回の操作で複数のアセットをロードする場合、個々のアセットがロードされる順番は、ロード関数に渡すリスト内のキーの順番と一致するとは限りません。
結合操作で選択されるアセットを、そのロードに使用するキーと関連付ける必要がある場合は、以下の 2 つのステップで操作を実行できます。
- アセットキーのリストを使用して、IResourceLocation インスタンスをロードします。
- IResourceLocation インスタンスをキーとして使用して、個々のアセットをロードします。
IResourceLocation オブジェクトにはキー情報が含まれるため、例えばディクショナリを保持して、キーとアセットを相互に関連付けることができます。LoadAssetsAsync などのロード関数を呼び出した場合は、操作によって、キーに対応する IResourceLocation インスタンスが最初に検索され、それを使用してアセットがロードされます。IResourceLocation を使用してアセットをロードすると、この操作の最初のステップが省略されます。つまり、2 つのステップで操作を実行しても、処理量が大幅に増えることはありません。
以下の例では、キーのリストに対応するアセットをロードし、それらをアドレス (PrimaryKey) によってディクショナリに挿入します。最初に、指定されたキーに対するリソースの場所をロードします。この操作が完了したら、それぞれの場所のアセットをロードします。このとき、Completed イベントを使用して、個々の操作ハンドルをディクショナリに挿入します。これらの操作ハンドルは、アセットをインスタンス化するためと、それらが不要になったら解放するために使用できます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
internal class LoadWithLocation : MonoBehaviour
{
public Dictionary<string, AsyncOperationHandle<GameObject>> operationDictionary;
public List<string> keys;
public UnityEvent Ready;
IEnumerator LoadAndAssociateResultWithKey(IList<string> keys)
{
if (operationDictionary == null)
operationDictionary = new Dictionary<string, AsyncOperationHandle<GameObject>>();
AsyncOperationHandle<IList<IResourceLocation>> locations
= Addressables.LoadResourceLocationsAsync(keys,
Addressables.MergeMode.Union, typeof(GameObject));
yield return locations;
var loadOps = new List<AsyncOperationHandle>(locations.Result.Count);
foreach (IResourceLocation location in locations.Result)
{
AsyncOperationHandle<GameObject> handle =
Addressables.LoadAssetAsync<GameObject>(location);
handle.Completed += obj => operationDictionary.Add(location.PrimaryKey, obj);
loadOps.Add(handle);
}
yield return Addressables.ResourceManager.CreateGenericGroupOperation(loadOps, true);
Ready.Invoke();
}
void Start()
{
Ready.AddListener(OnAssetsReady);
StartCoroutine(LoadAndAssociateResultWithKey(keys));
}
private void OnAssetsReady()
{
float x = 0, z = 0;
foreach (var item in operationDictionary)
{
Debug.Log($"{item.Key} = {item.Value.Result.name}");
Instantiate(item.Value.Result,
new Vector3(x++ * 2.0f, 0, z * 2.0f),
Quaternion.identity, transform);
if (x > 9)
{
x = 0;
z++;
}
}
}
private void OnDestroy()
{
foreach (var item in operationDictionary)
{
Addressables.Release(item.Value);
}
}
}
ロード関数では、ResourceManager.CreateGenericGroupOperation を使用してグループ操作を作成します。これにより、すべてのロード操作が終了した後に関数を続行することが可能になります。この場合、関数は "Ready" イベントを送信して、ロードされたデータが使用可能になったことを他のスクリプトに通知します。
AssetReference のロード
AssetReference クラスには、LoadAssetAsync という独自のロードメソッドがあります。
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadFromReference : MonoBehaviour
{
// Assign in Editor
public AssetReference reference;
// Start the load operation on start
void Start()
{
AsyncOperationHandle handle = reference.LoadAssetAsync<GameObject>();
handle.Completed += Handle_Completed;
}
// Instantiate the loaded prefab on complete
private void Handle_Completed(AsyncOperationHandle obj)
{
if (obj.Status == AsyncOperationStatus.Succeeded)
{
Instantiate(reference.Asset, transform);
}
else
{
Debug.LogError("AssetReference failed to load.");
}
}
// Release asset when parent object is destroyed
private void OnDestroy()
{
reference.ReleaseAsset();
}
}
Addressables.LoadAssetAsync メソッドへのキーとして、AssetReference オブジェクトを使用することもできます。AssetReference に割り当てられたアセットの複数のインスタンスをスポーンする必要がある場合は、Addressables.LoadAssetAsync を使用します。これにより、各インスタンスを解放するために使用できる操作ハンドルが提供されます。
AssetReference の使用の詳細については、AssetReference を参照してください。
シーンのロード
アドレスまたはその他の Addressable キーオブジェクトによって Addressable シーンアセットをロードするには、Addressables.LoadSceneAsync メソッドを使用します。
Note
Addressables.LoadSceneAsync は、内部的に Unity エンジンの SceneManager.LoadSceneAsync メソッドを使用します。SceneManager.LoadSceneAsync の動作に影響する API は、Addressables.LoadSceneAsync にも同じように影響する可能性があります。これには、例えば Application.backgroundLoadingPriority が該当します。
メソッドの残りのパラメーターは、SceneManager.LoadSceneAsync メソッドで使用されるパラメーターに対応します。
- loadMode: ロードされたシーンを現在のシーンに加えるか、現在のシーンをアンロードして置き換えるかを指定します。
- loadSceneParameters: loadMode と localPhysicsMode が含まれます。シーンをロードするときに、2D と 3D の物理シーンのどちらを作成するかを指定するために使用します。
- activateOnLoad: ロードが終了した直後にシーンをアクティベートするか、SceneInstance オブジェクトの ActivateAsync メソッドを呼び出すまで待機するかを指定します。AsyncOperation.allowSceneActivation オプションに対応します。デフォルトは true です。
- priority: シーンのロードに使用される AsyncOperation の優先度。AsyncOperation.priority オプションに対応します。デフォルトは 100 です。
Warning
activateOnLoad
パラメーターを false に設定すると、シーンをアクティベートするまで、他のあらゆる Addressable アセットのロードを含めて AsyncOperation キューがブロックされます。シーンをアクティベートするには、LoadSceneAsync から返される SceneInstance の ActivateAsync メソッドを呼び出します。詳細については、AsyncOperation.allowSceneActivation を参照してください。
以下の例では、シーンを追加でロードします。このコンポーネントでは、シーンをロードし、操作ハンドルを保存して、親ゲームオブジェクトが破棄されるときに、その操作ハンドルを使用してシーンをアンロードして解放します。
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
internal class LoadSceneByAddress : MonoBehaviour
{
public string key; // address string
private AsyncOperationHandle<SceneInstance> loadHandle;
void Start()
{
loadHandle = Addressables.LoadSceneAsync(key, LoadSceneMode.Additive);
}
void OnDestroy()
{
Addressables.UnloadSceneAsync(loadHandle);
}
}
追加の例については、Addressables-Sample リポジトリ内の [Scene Loading プロジェクト] を参照してください。
LoadSceneMode.Single を使用してシーンをロードした場合は、Unity ランタイムによって現在のシーンがアンロードされ、Resources.UnloadUnusedAssets が呼び出されます。詳細については、[Addressable アセットの解放] を参照してください。
Note
エディターでは、現在のプロジェクトにいつでもシーンをロードできます。これは、使用できないリモートバンドルにシーンがパッケージ化されていて、再生モードスクリプトを Use Existing Build に設定している場合にも当てはまります。エディターは、アセットデータベースを使用してシーンをロードします。
場所によるアセットのロード
アドレス、ラベル、または AssetReference によって Addressable アセットをロードする場合は、Addressables システムによって、最初にアドレスのリソースの場所が検索され、それらの IResourceLocation インスタンスを使用して、必要な AssetBundle とすべての依存関係がダウンロードされます。アセットのロード操作は、2 つのステップで実行できます。最初に、LoadResourceLocationsAsync を使用して IResourceLocation オブジェクトを取得します。次に、それらのオブジェクトをキーとして使用して、アセットをロードまたはインスタンス化します。
IResourceLocation オブジェクトには、1 つ以上のアセットのロードに必要な情報が含まれています。
LoadResourceLocationsAsync メソッドが失敗することはありません。指定されたキーをアセットの場所に解決できない場合は、空のリストが返されます。type
パラメーターに特定のタイプを指定すると、関数によって返されるアセットの場所のタイプを限定できます。
以下の例では、"knight" または "villager" というラベルを持つすべてのアセットの場所をロードします。
AsyncOperationHandle<IList<IResourceLocation>> handle
= Addressables.LoadResourceLocationsAsync(
new string[] {"knight", "villager"},
Addressables.MergeMode.Union);
yield return handle;
//...
Addressables.Release(handle);
サブオブジェクトの場所のロード
コンテンツカタログのサイズを小さくし、ランタイムパフォーマンスを高めるために、サブオブジェクトの場所はランタイム時に生成されます。サブオブジェクトを持つアセットのキーを使用して LoadResourceLocationsAsync を呼び出す場合、タイプを指定しないと、すべてのサブオブジェクトとメインオブジェクト (該当する場合) に対して IResourceLocation インスタンスが生成されます。同様に、AssetReference がサブオブジェクトを持つアセットを指している場合、使用するサブオブジェクトを指定しないと、すべてのサブオブジェクトに対して IResourceLocation が生成されます。
例えば、FBX アセットの場所をロードするときに "myFBXObject" というアドレスを使用すると、ゲームオブジェクト、メッシュ、マテリアルの 3 つのアセットの場所が取得される可能性があります。アドレスにタイプを指定して "myFBXObject[Mesh]" とした場合は、メッシュオブジェクトのみが取得されます。タイプを指定するには、LoadResourceLocationsAsync 関数の type
パラメーターを使用することもできます。
Addressable アセットからのオブジェクトのインスタンス化
プレハブなどのアセットをロードしたら、Instantiate を使用してそのインスタンスを作成できます。また、アセットをロードし、Addressables.InstantiateAsync でインスタンスを作成することもできます。オブジェクトをインスタンス化するこれらの 2 つの方法の主な違いは、アセットの参照カウントへの影響にあります。
InstantiateAsync を使用する場合、ロードされたアセットの参照カウントは、メソッドを呼び出すたびに 1 増加します。したがって、プレハブを 5 回インスタンス化すると、プレハブアセットとそのすべての依存関係の参照カウントは 5 増加します。各インスタンスは、ゲーム内で破棄されるに従って個別に解放できます。
LoadAssetAsync と Object.Instantiate を使用する場合、アセットの参照カウントは、最初のロードで 1 だけ増加します。ロードされたアセット (またはその操作ハンドル) を解放して参照カウントがゼロになると、アセットがアンロードされ、追加でインスタンス化されたすべてのコピーでもサブアセットが失われます。このようなコピーはシーン内にゲームオブジェクトとして残りますが、それらが依存しているメッシュ、マテリアル、その他のアセットはなくなります。
どちらのシナリオがより適しているかは、オブジェクトコードの編成方法によって異なります。例えば、プレハブの敵アセットをゲームレベルにスポーンするために、それらのプールを提供する単一のマネージャーオブジェクトがある場合は、マネージャークラスに単一の操作ハンドルを格納し、レベルの完了時にそれを使用して、すべてのアセットを解放すると便利です。別の状況では、アセットを個別にインスタンス化して解放することが適している場合もあります。
以下の例では、InstantiateAsync を呼び出してプレハブをインスタンス化しています。この例では、インスタンス化したゲームオブジェクトに、ゲームオブジェクトが破棄されたときにアセットを解放するコンポーネントを加えます。
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class InstantiateFromKey : MonoBehaviour
{
public string key; // Identify the asset
void Start()
{
// Load and instantiate
Addressables.InstantiateAsync(key).Completed += instantiate_Completed;
}
private void instantiate_Completed(AsyncOperationHandle<GameObject> obj)
{
// Add component to release asset in GameObject OnDestroy event
obj.Result.AddComponent(typeof(SelfCleanup));
}
}
// Releases asset (trackHandle must be true in InstantiateAsync,
// which is the default)
internal class SelfCleanup : MonoBehaviour
{
void OnDestroy()
{
Addressables.ReleaseInstance(gameObject);
}
}
InstantiateAsync の呼び出し時には、Object.Instantiate メソッドと同じオプションに加えて、以下の追加パラメーターを指定できます。
- instantiationParameters: このパラメーターは、インスタンス化オプションを指定するために使用できる InstantiationParameters 構造体を受け取ります。これを使用すると、InstantiateAsync の呼び出しごとに各オプションを指定する必要がなくなります。これは、同じ値を使用して複数のインスタンス化を行う場合に便利です。
- trackHandle: true (デフォルト) の場合、Addressables システムは、インスタンス化されたインスタンスの操作ハンドルを追跡します。これにより、Addressables.ReleaseInstance メソッドでアセットを解放できます。false の場合、操作ハンドルは追跡されません。インスタンスを破棄するときに解放するために、InstantiateAsync から返されたハンドルへの参照を開発者側で保存する必要があります。
非同期ロード
Addressables システム API は非同期であり、操作の進行と完了の管理に使用するための AsyncOperationHandle を返します。 Addressables は、コンテンツの場所に依存しない設計です。コンテンツは、最初にダウンロードしたり、時間のかかる他の方法を使用したりする必要がある場合があります。同期実行を強制する方法の詳細については、[同期的な Addressables] を参照してください。
初めてアセットをロードする場合、処理が完了するのは少なくとも 1 フレーム後になります。ロードが完了するまで待機するには、以下に示すように、いくつかの異なる方法を使用できます。 コンテンツが既にロードされている場合の実行時間は、以下に示す各種の非同期ロードオプションによって異なる可能性があります。
- Coroutine: 実行の継続前に、常に少なくとも 1 フレーム遅延します。
- Completed callback: コンテンツがまだロードされていない場合は、少なくとも 1 フレーム。それ以外の場合は、同じフレームでコールバックが呼び出されます。
- AsyncOperationHandle.Task の待機: コンテンツがまだロードされていない場合は、少なくとも 1 フレーム。それ以外の場合は、同じフレームで実行が継続されます。
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class AsynchronousLoading : MonoBehaviour
{
private string address = "tree";
private AsyncOperationHandle loadHandle;
// always minimum of 1 frame
IEnumerator LoadAssetCoroutine()
{
loadHandle = Addressables.LoadAssetAsync<GameObject>(address);
yield return loadHandle;
}
// minimum of 1 frame for new asset loads
// callback called in current frame for already loaded assets
void LoadAssetCallback()
{
loadHandle = Addressables.LoadAssetAsync<GameObject>(address);
loadHandle.Completed += h =>
{
// Loaded here
};
}
// minimum of 1 frame for new asset loads
// await completes in current frame for already loaded assets
async void LoadAssetWait()
{
loadHandle = Addressables.LoadAssetAsync<GameObject>(address);
await loadHandle.Task;
}
private void OnDestroy()
{
Addressables.Release(loadHandle);
}
}
Addressable アセットの解放
Addressables システムでは、アセットが使用中かどうかを判断するために参照カウントが使用されるため、ロードやインスタンス化を行ったアセットはすべて、不要になったら解放する必要があります。詳細については、[メモリ管理] を参照してください。
シーンをアンロードすると、その AssetBundle もアンロードされます。これにより、元のシーンから別のシーンに移動されたゲームオブジェクトなど、シーンに関連付けられたアセットがアンロードされます。
LoadSceneMode.Single モードを使用してシーンをロードすると、Unity ランタイムによって UnloadUnusedAssets
が自動的に呼び出されることに注意してください。シーンとそのアセットがアンロードされないようにするには、シーンの手動アンロードが必要になるまで、シーンのロード操作ハンドルへの参照を維持します。これを行う方法の 1 つは、ロード操作ハンドルに対して ResourceManager.Acquire を使用することです。Object.DontDestroyOnLoad や HideFlags.DontUnloadUnusedAsset など、アセットを保持する従来のメソッドは機能しません。
シーンとは別にロードした個々の Addressable アセットとその操作ハンドルは解放されません。これらのアセットを解放するには、Resources.UnloadUnusedAssets または UnloadAsset を呼び出す必要があります (例外として、trackHandle
を true (デフォルト) に設定した Addressables.InstantiateAsync を使用してインスタンス化した Addressable アセットは、すべて自動的に解放されます)。
シーンでの Addressable アセットの使用
シーン自体が Addressable である場合は、他のアセットと同じように、シーン内で Addressable アセットを使用できます。例えば、プレハブと他のアセットをシーンに配置したり、コンポーネントのプロパティにアセットを割り当てたりできます。Addressable でないアセットを使用すると、そのアセットはシーンの暗示的な依存関係となり、コンテンツビルドの作成時に、ビルドシステムによってシーンと同じ AssetBundle にパックされます (Addressable アセットは、所属するグループに従って、それ自体の AssetBundle にパックされます)。
Note
複数の場所で使用される暗示的な依存関係は、複数の AssetBundle とビルトインシーンデータで重複する可能性があります。不要な重複アセットを特定するには、Analyze ツールの Check Duplicate Bundle Dependencies ルールを使用します。
シーンが Addressable でない場合、シーン階層に直接追加した Addressable アセットはすべて暗示的な依存関係となります。これらのアセットは、既に Addressable グループに存在していても、Unity によってそのコピーがビルトインシーンデータに加えられます。これは、シーン内のゲームオブジェクト上のコンポーネントに割り当てられた、マテリアルなどのすべてのアセットでも同様です。
カスタムコンポーネントクラスでは、AssetReference フィールドを使用して、Addressable でないシーンで Addressable アセットを割り当てることができます。それ以外の場合は、[Address] と [Label] を使用して、ランタイム時にスクリプトからアセットをロードできます。シーンが Addressable であるかどうかにかかわらず、AssetReference をコードでロードする必要があることに注意してください。