テラシュールブログ

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

【Unity】AddressableでSprite ModeがMultipleなスプライトを使用する

AddressableでMultiSpriteを使用する方法についてです。

Unity 2019.1 Addressable 0.8.4 preview

追記:1.1.4で問題なく使えるようになりました

    async void Start()
    {
        var sprites = await Addressables.LoadAssetAsync<IList<Sprite>>("sprites").Task;

        foreach (var sprite in sprites)
        {
            Debug.Log(sprite.name);
        }
    }

マルチスプライト

Unityで2Dゲームを作るとき、2Dスプライトをパッキングすることがあります。 このとき、SpriteAtlasを使うか、パッキングしたテクスチャをSpriteEditorでスライスするかを選択出来ます。

f:id:tsubaki_t1:20190617230856j:plain

今回はSpriteをSpriteEditorでパッキングした場合の、Addressable対応についてです。

AddressableはSubAssetを直接ロード出来ない

結論から言えば、パッキングしたスプライトをロードすることは出来ません。
正確に言えば、SubAssetというカテゴリに当たるファイルを、直接指定してロードすることが出来ません。

これはTextureからSprite一覧を取り出したり、FBXからAnimationClipを抽出したり等です。

厄介な事にFastModeでは動いてしまうので一見問題が無いように見えますが、Packed Modeにすると動かない事が確認出来ます。例えば下のコードはPackedだと動きません。

public class LoadSprite : MonoBehaviour
{
    [SerializeField] int id;
    
    async void Start()
    {
        var sprites = await Addressables.LoadAssetAsync<Sprite[]>("sprite").Task; // packed だとエラー
       GetComponent<SpriteRenderer>().sprite = sprites[id];
    }
}

これはSpriteAnimationといった「アセットを使用しているだけ」のケースでは問題無いのですが、キャラクターアイコン等の「スプライトをケースバイケースで差し替える」といった場合に問題になるかもしれませn

解決方法

解決方法は、SubAssetでなくす事です。アプローチは幾つかあります。

アイディア1: スプライトを取り出す

テクスチャからSpriteを取り出します。例えばCtrl + Dで複製する等。実際にはエディター拡張でやるべきですが、まぁそれはそれ。

Spriteは「メッシュ&UV&テクスチャへの 参照 」の情報なので、複製して通常のアセットにしてやれば、普通にロードが可能です。

f:id:tsubaki_t1:20190617234443j:plain

アイディア2:SpriteAtlasに格納する

SpriteAtlasに格納する方法もあります。

SpriteAtlasにスプライトを設定すると、スプライトは再パッキングされます。そのかわり spriteAtlas.GetSprite(str) でスプライトを取得出来ます。

UVが既存のテクスチャから変化する点に注意です。またパック元になるテクスチャは必ず無圧縮にします。

f:id:tsubaki_t1:20190618000513j:plain

アイディア3:適当なScriptableObjectに参照させておく

UV等の情報を維持したままスプライトをロードしたい場合、適当なScriptableObjectから参照しておき、それをロードさせるというアイディアもあります。

例えば下のようなScriptableObjectを用意しておき、スプライトをすべて登録しておきます。

using UnityEngine;

[CreateAssetMenu(menuName ="SpriteRefs")]
public class Sprites : ScriptableObject
{
    public Sprite[] value;
}

f:id:tsubaki_t1:20190617235535j:plain

あとは普通にScriptableObjectをロードすれば、そのScriptableObjectが参照するSpriteを取得出来ます。

public class LoadSprite : MonoBehaviour
{
    [SerializeField] int id;
    
    async void Start()
    {
        var sprites = await Addressables.LoadAssetAsync<Sprites>("sprites").Task;
       GetComponent<SpriteRenderer>().sprite = sprites.value[id];
    }
}

感想

SubAssetがロード出来ないという問題があるので少々面倒ですが、一応回避方法はあるという事で。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp