#初めに
AddressableAssetSystemは皆さん使われていますか?使っていても使っていなくても、ものすごく便利な機能だと思うので使っていない人はぜひ導入を検討してみてください!
ここではAddressableAssetSystemについての最近使えるようになった機能についての解説と個人的に便利だと思う小技について解説を行っていきます。またコード自体はパブリックドメインとしておきますのでご自由にお使いください。
#機能の名前が変更になった(1.3.2以降)
以前の機能の名称から少し変更になりました。以下にその表を記載しておくので参考にしてください
1.3.3以前 | 1.3.3以降 |
---|---|
PlayMode | |
Fast Mode | Use Asset Database |
Virtual Mode | Simulate Groups |
Packed Mode | Use Existing Build |
BundledAssetGroupSchema | Content Packing & Loading |
ContentUpdateGroupSchema | Content Update Restriction |
Prepare for Content Update | Tools/Check for Content Update Restriction |
Build for Content Update | Update a Previous Build |
Profiler | Event Viewer |
#Addressables.PreloadDependenciesの名前の変更(0.6.6以降) | |
古いバージョンのAddressables.PreloadDependenciesはAddressables.DownloadDependenciesに変わりました。 |
#SpriteAtlasなどから個別にSpriteなどをAssetReferenceとして取り出せるようになった!(1.2.2以降)
この機能がどのように便利かというと、従来はSpriteAtlasからSpriteを取り出したいとき、GetSpriteで取り出すほかうまく動作させることが出来ませんでしたがこの機能があればAssetRefrenceにSpriteAtlasを指定するとサブアセットを取り出せるようになります。
いままで
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.U2D;
public class LoadImage : MonoBehaviour
{
[SerializeField] AssetReference target;
private void Start()
{
Addressables.LoadAssetAsync<SpriteAtlas>(target).Completed += Loaded;
}
private void Loaded(UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<SpriteAtlas> handle)
{
var atlas = handle.Result;
var sprite = atlas.GetSprite("利用したいイメージ");
}
}
と表記を行って、targetにはSpriteAtlasを入れてロードを行っていましたがこの新しい機能を使うとコード自体は単純な
using UnityEngine;
using UnityEngine.AddressableAssets;
public class LoadImage : MonoBehaviour
{
[SerializeField] AssetReference target;
private void Start()
{
Addressables.LoadAssetAsync<Sprite>(target).Completed += Loaded;
}
private void Loaded(UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<Sprite> handle)
{
var sprite = handle.Result;
}
}
Spriteを読み込むScriptでよくなり
SpriteAltasを指定するとEditorの画面でSpriteAltasに内包されているアセットを参照することが可能なため文字列での参照を行わずにSpriteを取得することができます。
#UnityCloludBuildでビルドできるようになった
そのままです
CloudBuildのConfig欄にAddressableの欄が追加されここだけで完結することが可能となりました。
コンテンツだけをビルドするモードもあるので
#Taskでawaitできるようになった
文字通りですAsyncOperationHandleがTaskを持つようになったので
using UnityEngine;
using UnityEngine.AddressableAssets;
public class ImageTaskLoad: MonoBehaviour
{
[SerializeField] AssetReference target;
async void Start()
{
Sprite image = await Addressables.LoadAssetAsync<Sprite>(target).Task;
}
}
というUnityっぽくないモダンなコードを書くことが出来るようになりました。
またもしUniTaskを使っている場合は
using System;
using System.Threading;
using UniRx.Async;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
public static class AddressableGetAwaiterExtention
{
public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> asset)
{
UniTaskCompletionSource<T> completionSource = new UniTaskCompletionSource<T>();
asset.Completed += (result) =>
{
switch (result.Status)
{
case AsyncOperationStatus.None:
break;
case AsyncOperationStatus.Succeeded:
completionSource.TrySetResult(result.Result);
break;
case AsyncOperationStatus.Failed:
completionSource.TrySetException(result.OperationException);
break;
default:
throw new ArgumentOutOfRangeException(result.OperationException.ToString());
}
// completionSource.TrySetResult(result.Result);
};
return completionSource.Task.GetAwaiter();
}
public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> asset,CancellationToken cancellationToken)
{
UniTaskCompletionSource<T> completionSource = new UniTaskCompletionSource<T>();
asset.Completed += (result) =>
{
cancellationToken.ThrowIfCancellationRequested();
switch (result.Status)
{
case AsyncOperationStatus.None:
break;
case AsyncOperationStatus.Succeeded:
completionSource.TrySetResult(result.Result);
break;
case AsyncOperationStatus.Failed:
completionSource.TrySetException(result.OperationException);
break;
default:
throw new ArgumentOutOfRangeException(result.OperationException.ToString());
}
};
return completionSource.Task.GetAwaiter();
}
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> enumerator)
{
return new UniTask<T>(enumerator.GetAwaiter<T>());
}
}
を利用することで直接await Addressables.LoadAssetAsync();とさせることもできます。
#参照カウントをOnDestroy時に減らす
AddressableAssetSystemでは参照カウントを使ってリソースの管理をしています。ので基本的にはReleaseを明示して開放を行います、少しでもその手間を省くためにUniRxを導入している環境では拡張メソッドを使って以下のように書くことが可能になります。
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.ResourceManagement.AsyncOperations;
using UniRx;
using UnityEngine.AddressableAssets;
public static class ReleaseAssetOnDestroyExtensions
{
public static AsyncOperationHandle ReleaseAssetOnDestroy(this AsyncOperationHandle handle, Component component)
{
component.OnDestroyAsObservable().Subscribe(_ => Addressables.Release(handle));
return handle;
}
}
#AssetReferenceを自分で作成する
AssetreferenceはSerializeFieldなどでUnity側から取得する以外にもguidをコンストラクタに代入することで作成することが出来ます。使い道としてはシーンのguidを取得して(Assetreference.RuntimeKey.ToString()で取得できます)保存、再起動時にそのシーンから再開ということなどが考えられます。
#AssetReferenceTypeRestrictionの代替
AssetReferenceTypeRestrictionは何故か(本当に謎)廃止されてしまったのでAssetReferenceTを継承して制限するしようとなってしまいました詳しくはテラシュールブログのここにまとめてあります。
#終わりに
Addressableは個人的には外部から取得できるSerializeFieldだと思っていて、若干の手間や特殊な挙動はありますがAssetBundleよりはとても敷居が低く使えるようにチューンされています皆さんもぜひ使っていきましょう!