目次
検証環境
- Unity 2019.4.3f1
- Addressables 1.8.5
概要
Addressable Asset System は、Prefab や Scene、Texture、Shader、Material、TextMeshPro の FontAsset などのリソースに付与したアドレス(Addresable Name)を使ってアセットを管理するシステム。
従来からある AssetBundle で、各自実装しなければならなかった「管理する仕組み」などを提供している。
ローカルから生のアセットやビルドしたAssetBundle、リモートに配置した AssetBundle など、読み込む配置先を設定で切り替えることで、統一的なインターフェースでアセットを扱うことができるようになる。
以前は Resources フォルダを使ったアセットの読み込みがあった。
こちらはメタ情報を付与したり、圧縮したり、リソースは変換されてアプリに埋め込まれる。現在は非推奨な方法。
StreamingAssets フォルダを使ってもアセットを読み込める。
こちらは余計な変換処理は挟まれないが、Resources フォルダと同様にアプリに埋め込まれる。
AssetDatabase はプロジェクトフォルダ内にあるアセットならどれでも読み込めるが UnityEditor でしか動かない。
AssetBundle は、予めアセットをプラットフォームごとにビルドして、サーバーや StreamingAssets フォルダに配置することで、ロードできるようになる。
外部に配置するためアプリの容量は小さくなるが、ビルドや管理に関する機能が提供されておらず各自で行う必要があり手間がかかってしまう。
インストール
Window > Package Manager を開いて、[Addressables] からインストールする。
アセットを読み込む
AssetDatabase を使って UnityEditor で読み込む
対象となるアセットの Inspector を開いて、[Addresable] にチェックを付ける。
もしくは、Window > Asset Management > Addresables > Groups を開いて、対象のアセットをドラッグ&ドロップする。
スクリプトからは Addresable Name
を用いてアセットをロードするため、任意の名前をつける。
Play Mode Scripts で [Use Asset Database (faster)] を選択すると、実行時に AssetDatabase
を使って直接アセットを読み込むことができる。
続いてスクリプトから読み込む方法。
public AssetReference assetReference;
async Task Start()
{
var m = await Addressables.LoadAssetAsync<Material>("Assets/Materials/PulseCopy.mat").Task;
var go = await Addressables.LoadAssetAsync<GameObject>("Assets/Prefabs/Sphere.prefab");
var o1 = Instantiate(go);
var o2 = await Addressables.InstantiateAsync("Assets/Prefabs/Sphere.prefab");
o2.GetComponent<Renderer>().material = m;
var o3 = await assetReference.InstantiateAsync();
}
void OnDestroy()
{
if (m != null)
Addressables.Release(m);
if (go != null)
Addressables.Release(go);
if (o2 != null)
Addressables.ReleaseInstance(o2);
if (o3 != null)
Addressables.ReleaseInstance(o3);
}
アセットのロードは、リリースと対となる。Addressables は内部で参照カウントによって管理しているため、GameObject
を単に Destroy
すると不整合が起きる。Addressables からロードしたアセットは必ずアンロードする。(手動自動は問わない)*1
アセットを読み込むには、アドレス(Addresable Name)を指定して Addressables.LoadAssetAsync()
を呼び出す。開放する時は、ロードの戻り値として受け取ったオブジェクトを Release()
に渡す。
GameObject
を生成するには2パターンある。
1つ目は Addressables.LoadAssetAsync()
してから Instantiate()
する方法。
こちらは前述した通りRelease()
でリリースする。
2つ目は直接 Addressables.InstantiateAsync()
する方法。
こちらは ReleaseInstance()
に GameObject
を渡す。
また、InstantiateAsync()
で生成された GameObject
のライフサイクルはシーンに紐づくので、シーンが破棄される時は同時自動で破棄される。
AssetReference
を使うと Inspector 上から Addressable に登録したアセットを設定できる。
ロード処理は非同期のため、戻り値である AsyncOperationHandle
の Task
プロパティを await
する。
UnitTask を使った場合は、InstantiateAsync()
を直接 await
できる。
Play Mode Script
- Use Asset Database (faster)
- Simulate Groups (advanced)
- Use Existing Build (requires built groups)
[Use Asset Database] は、AssetDatabase
を使って直接アセットをロードする。アセットのビルドが不要ですぐに実行できるが、 Addressables Event Viewer には AssetBundle の情報は表示されない。
[Simulate Groups] は、AssetDatabase
を使ってロードされるためアセットのビルドは不要だが、AssetBundle の依存関係を分析してシュミレーションすることができる。
[Use Existing Build] は、実際にビルドした AssetBundle をロードする。
AssetBundle をローカルから読み込む
Play Mode Script で [Use Existing Build] を選択する。
Addressables Groups ウィンドウでグループを選択すると、Inspector に設定が表示される。
Build Path と Load Path がどちらも Local になっていることを確認する。
Addressables Groups ウィンドウで New Build > Default Build Script を実行するとビルドが実行され AssetBundle が生成される。
スクリプトは、「基本的な使い方 (faster)」の項のものがそのまま使える。
AssetBundle をリモート(ローカルサーバー)から読み込む
Addressables Groups ウィンドウで Tools > Hosting Services を開いて、[Local Hosting] を作成する。
任意の [Service Name] を入力し、[Enable] にチェックを付けるとポートが割り当てられる。
続いて、Addressables Group ウィンドウで Profile > Manage Profiles で Addressables Profile ウィンドウを開いて、[Profile] を作成する。
任意の [Profile Name] を付けて、RemoteBuildPath に [LocalHostData/[BuildTarget]]、RemoteLoadPath に [http://[PrivateIpAddress]:[HostingServicePort]] と入力する。
Addressables Group ウィンドウで Profile から作成したプロファイルを選択する。
Addressables Group ウィンドウでグループを選択し、 Inspector 上で、 Build Path と Load Path に Remote を選択する。それぞれプロファイルで設定した内容が反映される。
同 Inspector から [Inspect Top Level Settings] をクリックして、AddressableAssetSettings を開く。
[Build Remote Catalog] にチェックを付け、Build Path と Load Path に Remote を選択する。
[Player Version Override] は指定しないとビルド日時で名前が生成されるため、ビルドするたびに新しいファイルが生成される。同じバージョン番号を指定しているうちはコンテンツカタログが上書き更新されるのでファイルが増え続けることはない。
Play Mode Script で [Use Existing Build] を選択する。
アセットをビルドすると、プロジェクト直下に LocalHostData
フォルダが作成され Catalog と AssetBundle が出力される。
スクリプトは、「基本的な使い方 (faster)」の項のものがそのまま使える。
AssetBundle をリモート(Azure Blob Storage)から読み込む
今回は、Azure Blob Storage にパブリックアクセスレベルを「BLOB」にした assets
という名前でコンテナを用意する。(危険なので使い終わったらさっさと除去しすること)
実際は SAS などを生成してセキリティに気をつける必要がある。
コンテナのプロパティから URL をメモしておく。
Addressables Groups ウィンドウで Create > Group > Packed Assets を選択して、グループを作成する。
任意のアドレス(Addressable Name)を付け、対象のアセットを登録する。
Group を選択して Inspector を開いて、Build Path と Load Path に Remote を選択する。
同 Inspector から Inspect Top Level Settings をクリックして、AddressableAssetSettings を開く。
[Build Remote Catalog] にチェックを付け、Build Path と Load Path に Remote を選択する。
Addressables Group ウィンドウで Profile > Manage Profiles で Addressables Profile ウィンドウを開いて、[Profile] を作成する。
任意の [Profile Name] を付けて、RemoteLoadPath に用意した Blob の URL を使ってhttps://xxx.blob.core.windows.net/assets/[BuildTarget]
と設定する。[BuildTarget] はビルドしたときのプラットフォームの文字列が入る。
Addressables Group ウィンドウで Profile から作成したプロファイルを選択する。
Play Mode Script で [Use Existing Build] を選択する。
アセットをビルドすると、プロジェクト直下に ServerData フォルダが作成され Catalog と AssetBundle が出力される。
作成した Catalog と AssetBundle を、Azure Blob Storage にアップロードする。
コンテナのパスなど RemoteLoadPath が変わった場合は再度アセットをビルドしてアップロードし直す。
スクリプトは、「基本的な使い方 (faster)」の項のものがそのまま使える。
グループ
フォルダをドラッグ&ドロップすることもできる。階層を維持する場合、フォルダに対してアドレス(Addresable Name)とラベルを設定できる。フォルダを指定してまとめて読み込むことはできなそう。 ラベルは配下のアセットにも適用されるので問題なくまとめて読み込むことができる。
アドレス(Addresable Name)もラベルも配下のアセットに個別に設定することはできないが、決められたアドレス(Addresable Name)を使えば個別に読み込むことは可能である。
フォルダが不要な場合はグループの直下に移動する。
グループとプロファイルの関係
Addressables Groups
グループでは、アセットにアドレス(Addresable Name)とラベルを付与して、グループに登録する。
アセットはいずれかのグループに所属し、複数のグループには登録できない。
グループの設定
グループ単位で AssetBundle 化されるため、グループの設定では、Build Path と Load Path が選択できたり、圧縮やプロバイダーの選択など、Packing や Loading などの設定ができる。
AddressableAssetSettings
Assets/AddressableAssetsData/AddressableAssetSettings
では、コンテンツカタログの設定ができる。コンテンツカタログを出力するかどうか、Build Path と Load Path をどこにするか、バージョンを上書きするかなどの設定が含まれる。
Addressables Profiles
プロファイルでは、ビルドとロードのパスをどこにするかの設定を作り分けられるようになっている。Unity Editor で実行する時、ローカルホストや開発サーバー、本番サーバーで実行する時などプロファイルを切り替えるだけでビルド先とロード先を変更できるようになる。
ビルド
各グループはグループ毎に Build Path / Load Path の設定を持つ。
コンテンツカタログも別途 Build Path / Load Path の設定を持ち、出力するように設定にする必要がある。
これらの Path は、選択した Profile によって値が切り替えられる。
コンテンツカタログは AddressableAssetSettings で Build Retemo Catalog にチェックを付けると生成されるようになる。
ビルドしたけど出力されていないと思った時は各グループ、コンテンツカタログの Build Path がどこになっているかを確認すると良い。
ラベル
アセットにラベルを付けてグルーピングすることができる。アセットでラベルを指定してまとめて読み込んだり、ダウンロードすることができる。
Addressables Groups ウィンドウで、Tools > Labels を開いて、ラベルを作成する。
Addressables Groups ウィンドウで任意のアセットにラベルを設定する。
Addressables.LoadAssetsAsync()
の引数にラベルを渡すとまとめてロードできる。(Assets
が複数形なので注意)
var objects = await Addressables.LoadAssetsAsync<Object>("Model", null);
foreach(var item in objects)
{
Debug.Log(item);
}
コンテンツカタログ
ビルド結果のファイル構成は以下の通り。
- catalog_<version>.hash
- catalog_<version>.json
- グループ毎の AssetBundle や依存関係のある AssetBundle など
hash ファイルは、コンテンツカタログの更新の有無を比較するために使われる。
hash ファイルに違いがある場合、リモートから json ファイルをロードする。
hash ファイルが同じ場合はローカルのキャッシュからロードされる。
これら hash ファイルと json ファイルの取得元やファイル名などの情報は、アプリのビルド時に settings.json を書き出され StreamingAssets に保存される。*2
アプリのビルドをしないと取得元を変更することができないため、[Player Version Override] はアプリのバージョンと同じにして、アプリが同じバージョンの間はコンテンツカタログの名前が変わらないようにして上書き更新し続けるのが良さそうである。
light11.hatenadiary.com
Bundle Mode (Packing Mode)
AssetBundle の分割単位を設定できる。
原則としてグループ毎に AssetBundle に分割され、グループ内であっても Scene とそれ以外では更に分割される。
- Pack Together
- Pack Separately
- Pack Together By Label
Pack Together はグループ単位でまとめられる。同じグループでも Scene は別になる。
Pack Separately はアドレス単位ですべて個別に分割される。
Pack Together By Label はラベル単位で分割される。同じグループでも Scene は別になる。また、ラベルの付いていないアセットはまとめて1つの AssetBundle になる。
Window > Asset Management > Addressables > Analyze を開くと分割の内容を解析できる。(前述の画像)
light11.hatenadiary.com
キャッシュ
設定
Project ウィンドウで Addressables > Initialization > Cache Initialization Settings を追加する。
設定した CacheInitializationSettings を AddressableAssetSettings の Initialization Objects に追加する。
プロパティ |
内容 |
Compress Bundles |
キャッシュに保存される AssetBundle を LZ4 形式で圧縮するかどうか |
Cache Directory Override |
キャッシュの保存先 |
Expiration Delay |
キャッシュの保存期間(最大12960000秒(150日)) |
Limit Cache Size |
最大キャッシュサイズを有効にするかどうか |
保存先
Unity Editor (Windows)
リモートのコンテンツカタログがキャッシュされる場所。
C:\Users\<UserName>\AppData\LocalLow\<CompanyName>\<ProductName>\com.unity.addressables
ダウンロードした AssetBundle がキャッシュされる場所。
C:\Users\<UserName>\AppData\LocalLow\Unity\<CompanyName>_<ProductName>
グループの設定に以下の項目がある。(機能は未調査)
参考:qiita.com
HoloLens2
リモートのコンテンツカタログがキャッシュされる場所。
C:\Data\Users\<UserName>\AppData\Local\Packages\<ApplicationName_hash>\LocalState\com.unity.addressables
ダウンロードした AssetBundle がキャッシュされる場所。
C:\Data\Users\<UserName>\AppData\Local\Packages\MR-<ApplicationName_hash>LocalState\UnityCache\Shared
削除
ダウンロードした AssetBundle のキャッシュを削除する。コンテンツカタログは削除されない。
Caching.ClearCache();
補足
Addressables Event Viewer で参照カウントを可視化する
Addressables Event Viewer (旧名 RM Profiler)を使うと参照カウントを可視化したり、アセットが含まれている AssetBundle を確認することができる。
機能を有効にするには、Window > Asset Management > Addressables > Settings を開いて、[Send Profiler Events] にチェックを付ける。
light11.hatenadiary.com
Addressables で NullReferenceException
NullReferenceException when using Addressables.DownloadDependenciesAsync(label) - Unity Forum
Addressables
のメソッドを呼び出して Task
を await
しようとした時に Task
自体が null
で NullReferenceException
が発生する場合があるらしい。
以下のようにラップしてしまう拡張メソッドを用意するか、await
して取り出すような拡張メソッドを用意する。
public static class AsyncOperationHandleExtensions
{
public static Task<T> NotNullTask<T>(this AsyncOperationHandle<T> handle)
=> handle.Task ?? Task.FromResult(handle.Result);
}
別プロジェクトで作成したコンテンツカタログを読み込む
var catalog = await Addressables.LoadContentCatalogAsync("http://169.254.37.215:12345/catalog.json");
await Addressables.InstantiateAsync("Assets/Prefabs/Sphere.prefab");
tsubakit1.hateblo.jp
ダウンロード処理のカスタマイズ
light11.hatenadiary.com
便利ツール
- EZAddresser
- Unity Addressable Importer
Addressables クラス
await Addressables.InitializeAsync();
await Addressables.LoadContentCatalogAsync("CatalogPath");
await Addressables.GetDownloadSizeAsync("key");
await Addressables.DownloadDependenciesAsync("key");
参考