しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Unity の Addressables でダウンロードアセットのデータサイズを取得する

※ これは 2022/09/02 時点の Unity 2022.1.15f1 Addressables v1.20.5 Windows 11 の情報です

最新版では動作が異なる可能性がありますのでご注意ください

前回引き続き Addressables を使いつつ、今回はリモートアセットのダウンロードサイズをダウンロード前に取得して表示したい

前回のリモートアセット更新に追加する形で、ダウンロードする画像アセットのサイズを取得する

matatabi-ux.hateblo.jp

やることは Test.cs の変更だけ

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    [SerializeField]
    private Image cafeImage = null;

    [SerializeField]
    private Image mochaImage = null;

    private Image thumbnailImage = null;

    private AsyncOperationHandle<Sprite> handle = default;

    private const string RemoteLoadPath = @"http://localhost/StandaloneWindows64/{0}/";
    private static string OriginalRootPath = null;
    private static string CurrentRootPath = null;

    private AssetBundle currentBundle = null;
    private string currentName = null;
    private Hash128 currentHash = default;

    public async void Start()
    {
        // Addresables 初期化
        var locator = await Addressables.InitializeAsync().Task;

        // 初期化時のルートパス取得
        var info = Addressables.GetLocatorInfo(locator.LocatorId);
        OriginalRootPath = info.HashLocation.InternalId.Replace(Path.GetFileName(info.HashLocation.InternalId), "");

        // アドレス書き換えを有効化
        Addressables.InternalIdTransformFunc = MylIdTransform;

        await SetCatAsset("Cafe");
    }

    private static string MylIdTransform(IResourceLocation location)
    {
        // ルートパスを置き換える
        if (location.InternalId.StartsWith(OriginalRootPath))
        {
            return location.InternalId.Replace(OriginalRootPath, CurrentRootPath);
        }
        return location.InternalId;
    }

    private async Task SetCatAsset(string catName)
    {
        Cat.Name = catName;

        this.thumbnailImage = catName switch
        {
            "Cafe" => this.cafeImage,
            "Mocha" => this.mochaImage,
            _ => null
        };

        // ルートパスの再評価
        CurrentRootPath = string.Format(RemoteLoadPath, Cat.Name);

        // リモートカタログに変更があれば更新する
        var catalogs = await Addressables.CheckForCatalogUpdates(true).Task;
        if (catalogs != null && catalogs.Count > 0)
        {
            var locators = await Addressables.UpdateCatalogs(catalogs, true).Task;

            // リモートアセットの取得先を確認
            if (locators[0].Locate("Assets/Sprites/cat.png", typeof(Sprite), out var locations))
            {
                // リモートアセットの URL は Dependencies に入っている
                foreach(var location in locations[0].Dependencies)
                {
                    UnityEngine.Debug.Log($"--------------> uri={Addressables.InternalIdTransformFunc(location)}");

                    if (location.Data is AssetBundleRequestOptions sizeData)
                    {
#if UNITY_EDITOR
                        EditorUtility.DisplayDialog($"{location.PrimaryKey}", $"{sizeData.BundleSize:#,0} bytes", "OK");
#endif
                    }
                }

                var option =  locations
                        .SelectMany(x => x.Dependencies)
                        .Select(x => x.Data)
                        .OfType<AssetBundleRequestOptions>()
                        .First();
                var newName = option.BundleName;
                var newHash = Hash128.Parse(option.Hash);

                if (this.currentBundle != null)
                {
                    UnityEngine.Debug.Log($"--------------> {this.currentName}{this.currentHash}】 -> {newName}{newHash}】");

                    // 同じ名前で内容(Hash)が異なる AssetBundle がないか調べる
                    if (this.currentName.Equals(newName) && this.currentHash != newHash)
                    {
                        // アセットのインスタンスは残したまま、古い AssetBundle を解放してキャッシュからも破棄する
                        this.currentBundle.Unload(false);
                        if (Caching.IsVersionCached(newName, this.currentHash))
                        {
                            Caching.ClearCachedVersion(newName, this.currentHash);
                        }
                    }
                }

                // BundleName と Hash を保持しておく
                this.currentName = newName;
                this.currentHash = newHash;
            }
        }

        AsyncOperationHandle download = default;
        try
        {
            // AssetBundle を抽出するために Load 前に Download
            download = Addressables.DownloadDependenciesAsync("Assets/Sprites/cat.png");
            var resources = await download.Task as List<IAssetBundleResource>;
            this.currentBundle = resources[0].GetAssetBundle();

            // Addressables 経由で Sprite を読み込んで表示
            this.handle = Addressables.LoadAssetAsync<Sprite>("Assets/Sprites/cat.png");
            this.thumbnailImage.sprite = await this.handle.Task;
        }
        finally
        {
            // Load が終わったら Download ハンドルは用済みなので解放
            if (download.IsValid())
            {
                Addressables.Release(download);
            }
        }
    }

    /// <summary>
    /// Mocha ボタン押下イベントハンドラ
    /// </summary>
    public async void OnMochaButtonClicked()
    {
        await SetCatAsset("Mocha");
    }

    public void OnDestroy()
    {
        // いらなくなったら handle を Release する
        if (this.handle.IsValid())
        {
            Addressables.Release(this.handle);
        }
    }
}

Addressables.GetDownloadSizeAsync() を使うとダウンロード済みだったり、今回みたいにアプリ実行中にアセット更新して差し替える場合ちゃんとサイズが取得できないことがある

そのため、下記の箇所で IResourceLocation.DataAssetBundleRequestOptions にキャストして直接 BundleSize を取得する

                    if (location.Data is AssetBundleRequestOptions sizeData)
                    {
#if UNITY_EDITOR
                        EditorUtility.DisplayDialog($"{location.PrimaryKey}", $"{sizeData.BundleSize:#,0} bytes", "OK");
#endif
                    }

これをお試し

Cafe 画像のサイズ

Mocha 画像のサイズ

同じアドレスのリモートアセットでも Cafe と Mocha で画像が異なるのでちゃんと別のサイズとして取得できた