Unity Addressables の使い方 (1.1.4) (Android)

間違っている部分もありそうだけど、一応意図したとおり動いたのでまとめておく。
Unity 2019.1.7f1を使用し、Editor、Android実機で確認。

目的

  • かなり前からResourcesは非推奨なので、代替としてAddressablesを使う。
  • ゲーム起動時にサーバーに接続し、新しいデータが存在していたら更新する。

目次

  1. Addressablesのインストール
  2. Addressablesの設定
  3. AssetBundleデータの作成
  4. データの更新
  5. データを確認してから更新
  6. Android実機で検証
  7. 注意点、その他

1.Addressablesのインストール

Window > Package Manager を開く。
Packagesタブの下の項目を[All packages]に。
Advanced >Show prview packages にチェック。
左下のLoading packages…が終わるまで待つ。結構長い。
Addressablesが表示されたらInstall.

インストールが終わったら、
Window > Asset Management >Addressables
Addressablesのビューが表示される。

[Create Addressables Settings]を実行。
ProjectビューにAddressableAssetDataが作成される。

2.Addressablesの設定

ProjectビューのAddressableAssetSettingsを設定する。

Player Version Override = 自分はアプリのビルドバージョンに合わせている。
Build Remote Catalog = true

Profiles の下の+ボタンから、新しくプロファイルを作る。デフォルトを上書きしてもよい。
Profile Entries の RemoteLoadPathを、ファイルを置くサーバーに変更。
例: https://myservertesttest.com/[BuildTarget]
※/[BuildTarget]は消さない。

Addressablesビューに移動し、Profile: Defaultを上記で作ったプロファイルに変更。

Labelsに使いたいラベル名を書く。
ここでは「MyAsset」というラベルを1つ作っておく。

続いて、Projectビュー、AssetGroupTemplatesフォルダ内にある、Packed Assets(テンプレート)を設定する。

▼BundledAssetGroupSchema
Bundled Asset Provider Type = BundledAssetProvider
Build Path = LocalBuildPath
Load Path = LocalLoadPath

▼ContentUpdateGroupSchema
Static Content = true

3. AssetBundleデータの作成

イラストやから画像を借りてくる。
image1 https://www.irasutoya.com/2015/10/blog-post_65.html
image2 https://www.irasutoya.com/2018/09/blog-post_845.html
image3 https://www.irasutoya.com/2014/01/blog-post_3235.html

テスト用にフォルダやImageを作成していく。

Projectビューに AssetDataというフォルダを作り、image1を設置。
Addressablesビューにも同様のフォルダを作り、Projectビューからimage1をドラッグドロップ。

Addressablesビューで Build > Build Player Content を実行し、AssetBundleデータを作成。

HierarchyビューにUI > Imageを作成。

画面にimage1を表示するためのプログラムを書く。

public class ImageLoader : MonoBehaviour
{
    {
        ImageLoad();
    }

    void ImageLoad()
    {
  //画像のベースとなるImage
        Image image = GameObject.Find("Image").GetComponent<Image>();

  //画像データをロード、アドレスはAddressablesビューのAsset Addressで確認出来る
        Addressables.LoadAssetAsync<Sprite>("Assets/AssetData/image1.png").Completed += spriteData =>
        {
            image.sprite = spriteData.Result;
            image.preserveAspect = true; //アスペクト比を保つ
        };
    }
}

上記コードをCanvasあたりにアタッチする。

Addressablesビュー > Play Mode Script > Packed Play Mode に変更。
エディターでPlayする。

image1、赤ちゃんが表示される。

4.データの更新

ロードするアドレスはそのままで、表示される画像をimage2に変更する。

Projectビュー、AssetDataフォルダ内
image1をimage1bakにリネーム。
image2をドラッグドロップし、image1にリネーム。

Addressablesビュー、AssetDataフォルダ内
既存のimage1を削除。
ProjectビューAssetData内のimage1を、Addressables側にドラッグドロップ。
Labelsから、MyAssetラベルを付ける。

Addressablesビュー > Build > Prepare For Content Update を実行。

フォルダが表示されるのでaddressables_content_state.binを開く。

更新されるデータを確認し、[Apply Changes]

Addressablesビュー内にContent Updateというフォルダが作成され、image1がそこに設置される。

Addressablesビュー > Build > Build For Content Update を実行。

Unityプロジェクトのフォルダに「ServerData」というフォルダが出来る。
その中の「Android」フォルダをサーバーにアップロードする。
(Projectタブ内には表示されないため、Windowsのエクスプローラーからアクセス)

エディターでPlayする。
データがサーバーからダウンロードされ、更新されたimage1の子供(image2)が表示される。

5.データを確認してから更新

更新データがあるか確認し、あればダウンロード、更新した画像を表示。
無ければ既存の画像を表示させる。

先程のコードを以下のように修正する。
実装する時は
if (dataSize > 0)
の下あたりに、ダウンロードするかしないか、ユーザーに選択する表示を付けるのが普通かと思うが省略。

using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.UI;
using UnityEngine.ResourceManagement.AsyncOperations;

public class ImageLoader : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        //ImageLoad();

        Addressables.GetDownloadSizeAsync("MyAsset").Completed += updata =>
        {
            long dataSize = updata.Result;

            if (dataSize > 0)
            {
                //更新データあり、ダウンロード

                //MyAssetラベルの付いたデータをダウンロードする
                AsyncOperationHandle download = Addressables.DownloadDependenciesAsync("MyAsset");

                //ダウンロード中の処理
                StartCoroutine(DownloadWait(download));

                //ダウンロード完了時の処理
                download.Completed += op =>
                {                    
                    if (AsyncOperationStatus.Succeeded == download.Status)
                    {
                        //ダウンロード成功

                        //更新した画像を表示
                        ImageLoad();
                    }
                    else
                    {
                        //ダウンロード失敗
                    }
                };
            }
            else
            {
                //更新データ無し、既存の画像を表示
                ImageLoad();
            }
        };
    }
    IEnumerator DownloadWait(AsyncOperationHandle download)
    {
        while (!download.IsDone)
        {
            //ダウンロード中の処理を書く
            Debug.Log(download.PercentComplete); //※1.1.4ではPercentCompleteは機能していない

            yield return new WaitForSeconds(0.01f);
        }
    }

    void ImageLoad()
    {
        Image image = GameObject.Find("Image").GetComponent<Image>();

        Addressables.LoadAssetAsync<Sprite>("Assets/AssetData/image1.png").Completed += spriteData =>
        {
            image.sprite = spriteData.Result;
            image.preserveAspect = true;
        };
    }
}

ラベル名が一致する更新データのダウンロードサイズを取得している。
ラベル名など無しで、「更新データ全部のサイズ」というのは無理らしい。
Unityのフォーラムでそのうち実装するというコメントを見た気はする。
ラベルは複数付けられるので、今のところは共通のラベル名を全てのデータにつけておけばいいと思う。


Addressablesのコード修正
AndroidではGetDownloadSizeAsync()が正しく機能していないため、Packages/Addressables内の、
AssetBundleProvider.csを以下のように修正する。
(NG) if (!loc.InternalId.Contains("://"))

(OK) if (!loc.InternalId.StartsWith("http"))

参考 https://scrapbox.io/bexide-tech-blog/Addressables_0.6.8_%E4%B8%8D%E5%85%B7%E5%90%88%E3%81%BE%E3%81%A8%E3%82%81

6.Android実機で検証

実機検証のため、再度AssetBundleデータを作成していく。

Addressablesビュー内、Content Updateフォルダにあるimage1を、同ビュー内のAssetDataフォルダに移動する。
Content Updateフォルダは削除してよい。
Build > Build Player Contentを実行。

サーバーにあるAndroidフォルダ内のデータを削除。
Windowsエクスプローラーから、ServerData/Androidフォルダにあるcontentupdate…bundleを削除。
catalog….hash、catalog….jsonをサーバーにアップロード

Android実機を繋いでBuild and Run、image2の子供が表示される。

画像を更新する

Projectビュー、AssetDataフォルダ内
image1をimage2にリネーム。
image3をドラッグドロップし、image1にリネーム。

Addressablesビュー、AssetDataフォルダ内
image1を削除。
ProjectビューのAssetDataフォルダからimage1をドラッグドロップ。
LabelsからMyAssetラベルを付ける。

Addressablesビュー > Build > Prepare For Content Update。
[Apply Changes]を実行。
Build > Build For Content Updateを実行。

ServerData/Android内のファイル、hash、json、bundleをサーバーに上書きアップロード。

Android実機でアプリを起動する。
イラストが更新され、成長した子供がグレる。

7.注意点、その他

名前長すぎ問題
Projectビュー内での階層が深くなりすぎるとAsset Addressが長くなり、Build Player Content実行時に下記のようなエラーが出る。

DirectoryNotFoundException: Could not find a part of the path “Temp/com.unity.addressables/AssetBundles\めっちゃ長い名前_assets_all.bundle” or “Library/com.unity.addressables/StreamingAssetsCopy/aa/Android/Android\ めっちゃ長い名前 _assets_all_69177449bd8240b183cef2c1236045cd.bundle”

その場合はAddressablesビュー内のファイル上で右クリック、[Simplify Entry Names]を実行する。
Assets/AssetDataXXXXXX……/image1.png
というアドレスであれば、
image1.png
のようにアドレスが短縮される。
アドレス短縮し、Buildしたら、コードに書いてるアドレスも直さないと当然エラーが出る。

エディターで、パーティクル等がピンクになってしまう
エディター上で、Packed Mode実行時はそうなる仕様。
FastModeにするか、Android実機で確認すれば大丈夫。

一括で画像を表示したい場合のサンプルコード

Addressables.LoadAssetsAsync(new List { "ラベル名" }, null, Addressables.MergeMode.Intersection).Completed += data =>
{
    List images = new List();
    foreach(Sprite sprite in data.Result)
    images.Add(sprite);
};

アプリ更新時
Player Version Overrideを変更する。

Content Updateフォルダがあれば、中身を所定のフォルダに移す。
(以前のアプリバージョンでデータの更新があった際のアップデータを、新しいアプリバージョンに含める。
そうしないと、当然ダウンロードするアップデータがどんどん増えてしまう。)

Build Player Content を実行。
(Build Pipeline CacheとDetaBuildersをクリアしてからの方がベターかも。)

サーバーにServerDataフォルダからファイル(hash、json)をアップロード。
(以前のデータは残しておかないと、古いバージョンのアプリを使用してるユーザーが困る。)

不具合が出て色々試してもどうにもならない時
・AddressablesのReimport。
・AddressableAssetDataフォルダを削除 > AddressablesのReimport > データを作り直す。
・上記+Reimport All。

Leave a Reply

*