(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

ResourcesのシンプルさとAssetBundleの自由度を実現したAddressable Assetsとは【Unity】【Addressable Assets】【Unite 2018 Tokyo】


このエントリーをはてなブックマークに追加

この記事でのバージョン
Unity 2018.2.0f2


はじめに

Unityの鬼門の一つに「画像や音源等のリソースをどう管理するか」というものがあります。

「Resources使えば良いじゃないの?」と思う方もいるかもしれませんが、

実は公式で「Resourcesフォルダのベストプラクティスは使用しない事である」

なんて言われるぐらい推奨されていません。



じゃあ何を使えばいいのかと言えばAssetBundleなのですが、

「AssetBundleって難しいし、面倒くさいし、出来れば使いたくない!!」

というのが大方の意見かと思います。


それでもなくなくAssetBundleを使っていましたが、そんな現状を打破するために

なんとAddressable Assetsというリソース管理の新システムがUnity2018.2から使えるようになりました!

そして今回はそのAddressable Assetsを使ってみようという感じの記事です。


ちょっと触って見た感じ、簡単かつ便利で最高でした。

今まで自分たちで拡張して使いやすくしていた部分を公式で対応してくれた感があります。


なお、本記事は『Unite 2018 Tokyo』

「そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説」

を参考にさせてもらってます。

【講演者】
大前 広樹(日本担当ディレクター|ユニティ・テクノロジーズ・ジャパン合同会社)

【こんな人におすすめ】
・ゲームの実行時のリソース管理を改善したいプログラマ
・ゲーム開発中にアセットバンドルを利用することで開発効率が落ちることを解決したいプログラマ
・アセットバンドルという単語に複雑な感情を抱く方

【受講者が得られる知見】
・Addressable Asset Systemを用いたアセット管理の方法とそのメリット
・新しいリソースマネージャーを使ったデータのロード方法
・アセット関連の新ツールの活用方法




Addressable Assetsとは

まずはAddressable Assetsとはなんぞやという話ですが、簡潔に言うと

ResourcesのシンプルさとAssetBundleの自由度を実現したシステムです。


ResourcesやAssetBundleと比較しての利点を列挙してみると以下の通りです。

  • 全体的に変更に強く、気軽に使える
  • 使うのに複雑なコードが必要なく、簡単に使える。
  • アプリのサイズを小さく出来る(Resourcesはサイズが大きくなる)
  • アプリの起動が早くなる(Resourcesは起動に時間がかかる)
  • アプリ配信後に更新しやすい、パッチを当てやすい(AssetBundleと同じ)
  • (AssetBundleと違い)プレイモード開始前に自動で更新が行われる(Resourcesと同じ)
  • (AssetBundleと違い)プラットフォームごとにアセットを作る必要がない
  • アセットの実際の配置を自由に変更できる
  • ロードにはAdress(文字列)を使うが、そのAdressは自由に変更可能
  • Adressは手打ちでも使えるが、手打ちしなくて良い機能が付いてる(AssetReference)
  • 簡単に非同期でのロードや事前ロードができる
  • 開発時とリリース時の設定を簡単に切り替えられる
  • エディタ上での動作の変更可能(早いけど実機と動作が違う OR 遅いけど実機と同じ みたいな)
  • 補助用のツール類が色々


なお、色々と項目があって難しそうに見えるかもしれませんが、

やってる事は単純な事ばかりで、実際に使ってみるとさらに簡単に感じると思います。


導入

では、Addressable Assetsを実際に使っていきましょう。まずは導入です。

導入はPackage Managerを使うのですが、現状はGUI上でインストールするのではなく、

直接manifest.jsonに以下の項目を追加してインストールし、Unityエディタを再起動します。

"com.unity.addressables": "0.0.22-preview",

f:id:kan_kikuchi:20180717084818j:plain
f:id:kan_kikuchi:20180717084826j:plain


インストールが完了すると、

Window -> Asset Management -> Addressable Assetsでウィンドウが開けるようになります。


f:id:kan_kikuchi:20180717092118j:plain


最初に開いた時は設定を作成するかインポートするかのボタンが出るので、

Create Addressables Settingで設定を作成します。


f:id:kan_kikuchi:20180717092308j:plain


なお、Addressable Assets関連のファイルはAssets/AddressableAssetsDataにあります。

(間違って消さないように)


f:id:kan_kikuchi:20180717092316j:plain


導入はこれだけで完了です。

設定

次にAddressable Assetsで使うアセットを設定します。

一番簡単なのはドラック&ドロップで追加する方法です。

ただし、今の所はディレクトリを追加する事は出来ないみたいです。


f:id:kan_kikuchi:20180717092638j:plain


ウィンドウ左にあるAdrressがロードする時に使う文字列なのですが、任意に変更可能です。

Simplify Entry Nameで元ファイルの名前に、Renameで好きな文字列に出来ます。


f:id:kan_kikuchi:20180717102351g:plain


Pathはそのファイルがどこにあるかを表していて、自動更新されます。(実際のプログラムでは使わない)

なお、Pathが変わってもAddressは変わらないので、後からどこへ移動しても大丈夫です。


f:id:kan_kikuchi:20180717102507g:plain


また、Project上で選択し、InspectorのAddressableから設定する事も可能です。

チェックを有効にすればAddressable Assetsに含めて、Adrressもそのまま入力出来ます。


f:id:kan_kikuchi:20180717111929g:plain


さらに各アセットにLabelを付ける事も出来ます。(複数付ける事も可能)

設定したLabelの利用方法については後述します。


f:id:kan_kikuchi:20180717115820j:plain
f:id:kan_kikuchi:20180717115829j:plain


ロード

Addressable Assetsからアセットをロードする時はAddressables.LoadAssetやLoadAssetsを使います。

ResourcesのLoadと似ていますが、ファイルの指定にはパスではなく先程設定したAddressを使います。

また直接受け取るのではなく、受け取り用のデリゲートを登録して受け取る感じです。

さらにロードは非同期で行われます。(同期でのロードは出来ないっぽい?)

using UnityEngine;
using UnityEngine.AddressableAssets;//Addressablesを使うのに必要

public class NewBehaviourScript : MonoBehaviour {

  private void Start () {
    //character_13というAddressのSpriteを非同期でロード
    Addressables.LoadAsset<Sprite>("character_13").Completed +=  op => { 
      //op.ResultがロードしたSprite
      Debug.Log(op.Result.name);
    };
  }
	
}

f:id:kan_kikuchi:20180721111719j:plain
f:id:kan_kikuchi:20180717103621j:plain


なお、アセットが存在しない場合、nullが返るわけではなくエラーが出ます。

AssetReferenceでAddress(文字列)の直接入力を避ける

上記のようにロードする際にAddressを指定しますが、

直接入力してしまうとバグの温床になるので避けたい所です。

そんな悩みを解決する機能がAssetReferenceになります。


以下のようにAssetReferenceというクラスをInspectorで設定出来るようにすると、

プルダウンのGUIでAddressable Assetsに登録したアセットを指定出来るようになります。(検索も可能)

そしてこのAssetReference、なんとAddressの文字列の代わりになるのです!

using UnityEngine;
using UnityEngine.AddressableAssets;

public class NewBehaviourScript : MonoBehaviour {

  //Inspectorで設定出来るように
  [SerializeField]
  private AssetReference _texutreReference = null;

  private void Start () {
    //Addressの文字列の代わりにAssetReferenceを使ってロード
    Addressables.LoadAsset<Sprite>(_texutreReference).Completed +=  op => { 
      Debug.Log(op.Result.name);
    };
  }
	
}

f:id:kan_kikuchi:20180717103953j:plain


しかもAssetReferenceで設定すると、Pathはもちろんの事、Addressを変更してもそのまま使えます。

f:id:kan_kikuchi:20180717112439g:plain


なお、Addressable Assetsに登録してないアセットはプルダウンから選べませんが、

AssetReferenceの所にドラック&ドロップする事と自動で登録され、選択できます。


f:id:kan_kikuchi:20180717112605g:plain


AssetReferenceに設定出来るタイプを制限

AssetReferenceをそのまま使うと、何でも設定出来てしまうのでちょっと危ないです。

そんな時に使えるのが、AssetReferenceTypeRestrictionという属性です。

これを使えばAssetReferenceに設定出来るタイプを制限する事ができます。

//Textureだけ設定出来るように
[AssetReferenceTypeRestriction(typeof(Texture))]
public AssetReference _texutreReference = null;

f:id:kan_kikuchi:20180717110053j:plain


また、AssetReferenceLabelRestrictionという属性を使えば、Labelでの制限も可能です。

//PlayerというLabelが付いたアセットだけ設定出来るように
[AssetReferenceLabelRestriction("Player")]
public AssetReference _texutreReference = null;

f:id:kan_kikuchi:20180717110459j:plain
f:id:kan_kikuchi:20180717110506j:plain


もちろんAssetReferenceTypeRestrictionとAssetReferenceLabelRestriction組み合わせも可能です。

ただし、SerializeFieldとの併用は出来ないようです。(privateな変数には使えない)


事前ロード

Addressables.PreloadDependenciesを使うとあらかじめAssetをロードしておいて、

後からAddressables.LoadAssetを実行した時の処理時間を短くできます。

なお、Assetの指定にはLabelを使います。(指定したLabelが設定されたAssetを全てをロードする)

using UnityEngine;
using UnityEngine.AddressableAssets;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

  private IEnumerator Start() {

    //LabelにPrefabが設定されたAssetを全てをロードする
    var op = Addressables.PreloadDependencies("Prefab", null);
    op.Completed += (res) => {};

    yield return op;
  }

}



インスタンスの生成と破棄

Addressables.Instantiateを使えば、インスタンスをそのまま生成する事も可能です。

使い方はAddressables.LoadAssetと同じ感じです。

using UnityEngine;
using UnityEngine.AddressableAssets;

public class NewBehaviourScript : MonoBehaviour {

  //インスタンスかするPrefabをInspector上で設定
  [AssetReferenceTypeRestriction(typeof(GameObject))]
  public AssetReference PrefabReference = null;

  private void Start () {
    //Prefabからインスタンスを生成
    Addressables.Instantiate<GameObject>(PrefabReference).Completed +=  op => { 
      Debug.Log(op.Result.name);
    };
  }
	
}

f:id:kan_kikuchi:20180717111137j:plain
f:id:kan_kikuchi:20180717111055j:plain


ただし、Addressables.Instantiateで生成したインスタンスを削除する時には

DestroyでなくAddressables.ReleaseInstanceで削除する必要があります。

一応Destroyでも削除は出来てしまいますが、メモリリークが発生するっぽいです。(未確認)

//Destroyで削除しちゃダメ!
//Destroy(_player);

//削除する時はReleaseInstance
Addressables.ReleaseInstance(_player);



グループ(アセットのパッキング設定)

Addressable Assetsはグループごとにアセットがまとめられて(パッキングされて)ビルドされます。

なので別でビルドしたいアセットはグループを分ける必要があります。

(AssetBundleを分けるのと同じ感じ)


新しくグループを作りたい場合は右クリックのCreate New Groupからです。

作成出来るグループは3つありますが、基本はLocal Packed Contentで大丈夫です。

  • Advanced Packed Content: 任意の場所から任意のロード方法で
ロードする。
  • Local Packed Content : StreamingAssetsからロードする。
  • Remote Packed Content : 任意のサーバーからUnityWebRequestでロードする。


f:id:kan_kikuchi:20180717114417j:plain


Local以外だと同じグループ内でも別々にまとめる事も可能です。


f:id:kan_kikuchi:20180717114615j:plain


また、右クリックのメニューでグループ名を変えたり、削除や結合も出来ます。


f:id:kan_kikuchi:20180717114802j:plain
f:id:kan_kikuchi:20180717114809j:plain


なお、アセットが所属するグループを変えても、
 コードの変更は一切必要ありません。


プロファイル(開発中とリリース時で設定を変える)

開発中とリリース時で、ロード場所やビルド場所等を違う設定にしたい!

という時のためにプロファイルという機能も用意されている。


プロファイルはウィンドウの左上から追加や修正(Manage Profiles)、変更が行えます。


f:id:kan_kikuchi:20180717162413j:plain


新しく設定を作る場合はManager Profile list


f:id:kan_kikuchi:20180717162748j:plain
f:id:kan_kikuchi:20180717162849j:plain


いくつかの変数は最初から設定されてますが、自分で追加する事も可能です。


ResourcesやAssetBundleからの移行

ResourcesやAssetBundleを使っているけど、Addressable Assetsに移行したい!

という場合の話です。


Resourcesから移動は簡単で、ウィンドウ上でResourcesを選択し、

Move All Resources to groupを実行するだけ。

ただし、コードの修正は必要です。


f:id:kan_kikuchi:20180717165835j:plain


AssetBundleからの移行は以下のような手順で行います。

  • 自動的に設定済みAssetBundleをグループに変換(ダイアログあり)
  • AssetBundleにしているアセットをすべてAddressable Assetsにする
  • 現在の設定に合わせて、グループとプロファイルを設定する


こちらも、コードの修正は別途必要です。


Play Mode(エディター上でのAddressable Assetsの動作を変更)

ウィンドウの右上の歯車にはPlay Modeという項目があり、

ここからエディター上でのAddressable Assetsの動作を変更できます。


f:id:kan_kikuchi:20180717163053j:plain


なお、各項目の概要は以下の通り。

  • Fast : パッキングしない。速いがProfilerで得られる情報が少ない。
  • Virtual : パッキングしないが、パッキングした時の動作をシミュレートする。
  • Packed : パッキングする。遅いが実機での動作と同じ。



RM Profiler(アセットのロード状態の可視化)

Addressable Assetsの機能ではありませんが、ついでに

RM(Resource Manager) Profilerも紹介しておきます。


RM Profilerはどのアセットがいつロード、アンロードされているのか可視化出来たり、

参照カウント数や、どのような方式でロードされたのかの確認が可能です。


f:id:kan_kikuchi:20180721112046g:plain
【Unite Tokyo 2018】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説 - YouTube



なお、Window -> Asset Management -> Resource Profilerから開きます。


f:id:kan_kikuchi:20180717163443j:plain


おわりに

まさしく、全Unityユーザがこんなものを待ってたと言っても良いかもしれない、そんな代物でした。

これからはResourcesとAssetBundleの事は忘れて、迷わずAddressable Assetsを使っていきましょう。


とは言え、まだ登場したばかりで、これから色々と改善されていくと思います。

なので、進行中のプロジェクトで無理に切り替えるというよりは、

今のうちに慣れておく、新プロジェクトから使い始めるといった具合になりそうです。