テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】AddressableAssetSystemのAssetReferenceで、一覧表示するアセットを任意の型に制限したい

AddressableでのAssetReferenceで、任意のアセットを指定する方法についてです。

参照するアセットを絞り込んでおきたい

Addressableでアセットの遅延ロードを行いたい時、AssetReferenceでアセットを指定するというアイディアがあります。ただし、この時に登録しているアセットの数が膨大だと非常に面倒な事になります。

特にキャラクター名等、非常によく使うアドレスはテクスチャやオーディオ、その他諸々で複数登録されている事もありえるので、これを一覧から正しく選択しろというのはヒューマンエラーの元です。

f:id:tsubaki_t1:20190402231321j:plain
登録してる中から一つを探し出すのは面倒くさい

これに対応する理にかなったAPIとして [AssetReferenceTypeRestriction( type )]というAttributeがありましたが、何故かコレをver 0.6.6で廃止してくださいやがりましたので、他のアプローチを紹介します。

AssetReferenceTを使用する

解決法としては、AssetReferenceT<T>を継承したクラスを定義します。ジェネリックを使用したクラスはシリアライズ出来ないので、一々AssetReferenceT<T>を継承したクラスを定義する必要があります。

例えば下のような、ゲーム内でHPパラメーターを共有するScriptableObject:GameDataクラスがあったとして…

[CreateAssetMenu]
public class GameData : ScriptableObject
{
    [SerializeField] private int originalHP;
    public int HP { get; set; }

    private void OnEnable() => HP = originalHP; // 起動時にHPを初期化
    public bool IsDead() => HP < 0; // HPが0以下だったら死亡判定
    public void Damage(int value) => HP -= value; // ダメージを受けたらHPを減らす
}

f:id:tsubaki_t1:20190402232044j:plain
GameDataインスタンスはアセットとして複数のPrefabやSceneで共有可能にしてある状態

GameDataのみを使用可能なAssetReferenceTを用意します。

[System.Serializable] // Serializable属性が必ず必要
public class AssetReferenceGameData : AssetReferenceT<GameData> { }

あとは AssetReferenceの代わりにAssetReferenceGameData をAddressableの呼び出し元コードに設定します。

f:id:tsubaki_t1:20190402232525j:plain
指定したアセットしか表示されない

public class PlayerSpawn : MonoBehaviour
{
    // AssetReferenceではなくAssetReferenceGameData
    [SerializeField] AssetReferenceGameData gameData; 

    private void Awake()
    {
        enabled = false;
        gameData.LoadAsset().Completed += (data) =>  enabled = true;
    }

    private void OnDestroy()
    {
        gameData.ReleaseAsset();
    }

    private void Update()
    {
        // 取得済みのアセットを使用するが、こちらはキャストが必要
        ((GameData)gameData.Asset).Damage(1); 
    }
}

補足

ちなみにAssetReferenceTで指定出来る型は、UnityEngine.Objectのみみたいです。正確にはUnityEngine.Objectのアセットのみっぽく、Componentは指定出来ませんでした。ここにInterfaceが指定出来れば、色々な面で非常に楽になるんですが… ふっきゅんきゅんきゅん!

ついでにAssetReferenceLabelRestriction属性も廃止になり、ラベルを取得するAssetReferenceUILabelRestrictionクラスになりました。こちらは悪くないです。