2.4K Views
May 09, 18
スライド概要
講演者:大前 広樹(ユニティ・テクノロジーズ・ジャパン合同会社)
こんな人におすすめ
・ゲームの実行時のリソース管理を改善したいプログラマー
・ゲーム開発中にアセットバンドルを利用することで開発効率が落ちることを解決したいプログラマー
・アセットバンドルという単語に複雑な感情を抱く方
受講者が得られる知見
・Addressable Asset Systemを用いたアセット管理の方法とそのメリット
・新しいリソースマネージャーを使ったデータのロード方法
・アセット関連の新ツールの活用方法
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
DAY3 2018/05/09 そろそろ楽がしたい! 新アセットバンドルワークフロー & リソースマネージャー詳細解説 大前 広樹 ユニティ・テクノロジーズ・ジャパ ン
大前 広樹 ユニティ・テクノロジーズ・ジャパ ン Unityで働いている。 日本リージョンの指揮官役の他、 アセットバ ンドルまわりのツール開発なども助ける。 @pigeon6 hiroki-o [email protected]
Addressable Assets
Addressable Assets が解決したい問題 • アプリのダウンロードサイズを小さくしたい • アプリ配信後にコンテンツを動的に配信・更新したい • ゲームのファイル構成を、パッチを当てやすい形にしたい • 正直めんどいので楽がしたい
Addressable Assets の良さみ • Resourcesのシンプルさとアセットバ ンドルの自由度を実現 • ロードの依存関係や非同期処理を、複雑なコードを書かずに実現 • (Scriptable Build Pipelineで) ビルドやプレイモード時の動作もカスタマイズ可能に • 開発を助けるツール類の提供 • C#
Addressable Assets システム のステータス • 現在はプレビュー版 • Unity 2018.2から利用可能 • Package Managerでインストール { "dependencies": { "com.unity.addressables": "0.0.22-preview" • 今すぐさわってみるためには manifest.jsonの書き換えが必要 } }
Addressable Assets と Resourcesの違い • 固定の文字列(アドレス・パス)を使ったロードができる 共通点 • アセットの明示的なロードやアンロードができる • 非同期でのロード処理ができる • プレイモード開始前に自動で必要な更新が行われる • アセットの実際の配置を自由に変更できる 違い (進化点) • 自由な文字列をアドレスとしてつけられる • ラベルやAssetReferenceが使える • ロード方法やビルド方法などをカスタマイズできる
アドレスを使ったシステムへの疑問 ハードコード・・・? インスペクター・・・?
アドレスを使って開発しつつも、作りやすさを変えない 新機能:AssetReference • AssetReferenceを使うことで、アセット のアドレスをPrefabなどを指定するのと 同じような感覚で作ることができる • アーティストやゲームデザイナーにも 優しいインターフェイス • アドレス自体の打ち間違い防止 • 変更に強い
public AssetReference m_ref;
private GameObject m_character;
void Start ()
{
Addressables.Instantiate<GameObject>(m_ref)
.Completed += op => { m_character = op.Result; };
}
// Update is called once per frame
void Update () {
if (m_character != null)
{
m_character.transform.Rotate(90f * Time.deltaTime, 30f * Time.deltaTime, 60f * Time.deltaTime);
}
}
非同期でPrefabをロードして、完了したら回転するサンプルコード
インスタンス化したいアセット public AssetReference m_ref; を設定する private GameObject m_character; void Start () { Addressables.Instantiate<GameObject>(m_ref) .Completed += op => { m_character = op.Result; }; } Step 1
public AssetReference m_ref; private GameObject m_character; void Start () { Addressables.Instantiate<GameObject>(m_ref) .Completed += op => { m_character = op.Result; }; } GUIではリストから選択可能(フィルタ付き)
public AssetReference m_ref; private GameObject m_character; void Start () アセットを非同期にロードして、 インスタンス化する { Addressables.Instantiate<GameObject>(m_ref) .Completed += op => { m_character = op.Result; }; } Step 2
public AssetReference m_ref; private GameObject m_character; void Start () 全部が終了した時に呼びたい { 処理を登録する Addressables.Instantiate<GameObject>(m_ref) .Completed += op => { m_character = op.Result; }; } Step 3
[AssetReferenceTypeRestriction (typeof(GameObject))] public AssetReference m_ref; 型で限定する
[AssetReferenceLabelRestriction ("MyLabel")] public AssetReference m_ref; ラベルで限定する
アセットのロード・アンロードをマネージャーに任せる 新機能:参照カウント • Addressables には参照カウントの機能があり、参照カウントを使ってリソースやイ ンスタンスのロードや破棄ができる • Addressables でロードやインスタンス化をする際は、かならずAddressables の Release メソッドを使う
IEnumerator SpawnWaves()
{
yield return new WaitForSeconds(startWait);
while (true)
{
for (int i = 0; i < hazardCount; i++)
{
var hazardAddress = hazardLocations[Random.Range(0, hazardLocations.Count)];
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y,
spawnValues.z);
Quaternion spawnRotation = Quaternion.identity;
// ADDRESSABLES UPDATES
Addressables.Instantiate<GameObject>(hazardAddress, spawnPosition, spawnRotation);
敵キャラを出して、ぶつかったら自機と敵を破棄する
サンプルコード
yield return new WaitForSeconds(spawnWait);
}
yield return new WaitForSeconds(waveWait);
… … …
}
}
void OnTriggerEnter (Collider other)
{
… … …
Addressables.ReleaseInstance(other.gameObject);
Addressables.ReleaseInstance(gameObject);
}
}
IEnumerator SpawnWaves()
{
yield return new WaitForSeconds(startWait);
while (true)
{
for (int i = 0; i < hazardCount; i++)
{
var hazardAddress = hazardLocations[Random.Range(0, hazardLocations.Count)];
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y,
spawnValues.z);
Quaternion spawnRotation = Quaternion.identity;
// ADDRESSABLES UPDATES
Addressables.Instantiate<GameObject>(hazardAddress, spawnPosition, spawnRotation);
yield return new WaitForSeconds(spawnWait);
}
yield return new WaitForSeconds(waveWait);
… … …
}
}
敵のウェーブを生成する
void OnTriggerEnter (Collider other)
{
… … …
Addressables.ReleaseInstance(other.gameObject);
Addressables.ReleaseInstance(gameObject);
}
IEnumerator SpawnWaves()
{
yield return new WaitForSeconds(startWait);
while (true)
{
for (int i = 0; i < hazardCount; i++)
{
var hazardAddress = hazardLocations[Random.Range(0, hazardLocations.Count)];
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y,
spawnValues.z);
Quaternion spawnRotation = Quaternion.identity;
// ADDRESSABLES UPDATES
Addressables.Instantiate<GameObject>(hazardAddress, spawnPosition, spawnRotation);
yield return new WaitForSeconds(spawnWait);
}
yield return new WaitForSeconds(waveWait);
… … …
}
}
ぶつかったら両方のオブジェクトを破棄
void OnTriggerEnter (Collider other)
{
… … …
Addressables.ReleaseInstance(other.gameObject);
Addressables.ReleaseInstance(gameObject);
}
}
IEnumerator SpawnWaves()
{
yield return new WaitForSeconds(startWait);
while (true)
{
for (int i = 0; i < hazardCount; i++)
{
var hazardAddress = hazardLocations[Random.Range(0, hazardLocations.Count)];
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y,
spawnValues.z);
Quaternion spawnRotation = Quaternion.identity;
Addressablesから
インスタンス化したら・・・
// ADDRESSABLES UPDATES
Addressables.Instantiate<GameObject>(hazardAddress, spawnPosition, spawnRotation);
yield return new WaitForSeconds(spawnWait);
}
yield return new WaitForSeconds(waveWait);
… … …
}
}
void OnTriggerEnter (Collider other)
{
… … …
Addressables.ReleaseInstance(other.gameObject);
Addressables.ReleaseInstance(gameObject);
}
Addressablesからリリースする
Address チェックボックスで アドレスを追加 アドレスの設定
アドレスが自動で付与 AssetReferenceに D&D アドレスの設定
ドラッグ&ドロップで、アセットを Adressable化&グループ登録 アドレスやラベルを変更 Addressables ツール
パッキングとロードの方法を決める 新機能:グループ • アセットの配置場所やパッキング方法、ロード方法 などをグループごとに設定できる • アセットが所属するグループを変えても、 コードは一切変更の必要がない
パッキングとロードの方法を決める グループ設定インターフェイス アセットのまとめ方(単一vs個別) ビルド時に配置する場所 ロード時に使うパス
パッキングとロードの方法を決める 利用可能なグループ ローカル: StreamingAssetsに配置した アセットバ ンドルからロードする。 リモート: 任意のサーバーからUnityWebRequest でロードする。 アドバンスド: 任意の場所から任意のロード方法で ロードする。
アドレスとアセットの対応 新機能:コンテンツカタログ • アドレス、ラベル、グループの情報を記録するデータ • json形式で作成 • ビルド時やプレイモード変更時に自動生成 • リモートのグループ利用時には、リモートにも配置可能 • グループ単位ではなく、グローバルなカタログを生成 • hashによる更新検知 • アセットの依存関係もコンテンツカタログに記載
複数のリソースをまとめて扱う 新機能:ラベル • 「このステージを起動する前にあらかじめロードしておく べきリソースを、事前にロードしたい」 • アドレス付きアセットには、ラベルを設定することが できる • 一つのアドレスに、任意のラベルを複数つけることが できる • ラベルが該当するアセットを事前に一括ロード
“MyLabel”ラベルのアセットに
関連するアセットを全てロード
List<object> assets = new List<object>();
var op = Addressables.PreloadDependencies("MyLabel", null);
op.Completed += (res) =>
{
assets.AddRange(res.Result);
};
yield return op;
事前にアセットを先読みするサンプルコード
List<object> assets = new List<object>();
var op = Addressables.PreloadDependencies("MyLabel", null);
op.Completed += (res) =>
{
assets.AddRange(res.Result);
};
yield return op;
ロードが完了したら、
結果のデータをうけとる
事前にアセットを先読みするサンプルコード
List<object> assets = new List<object>();
var op = Addressables.PreloadDependencies("MyLabel", null);
op.Completed += (res) =>
{
assets.AddRange(res.Result);
};
yield return op;
ロードが完了するまで待つ
事前にアセットを先読みするサンプルコード
開発中とリリース時で異なる設定を使う 新機能:プロファイル
開発中とリリース時で異なる設定を使う プロファイルの用途 • ロード場所やビルド場所を、開発中やテスト中、リリース時などの用途に合わせて設定 • [BuildTarget] や {UnityEngine.Application.streamingAssetsPath} などの 変数が利用可能
開発中とリリース時で異なる設定を使う アクティブプロファイルの切替(エディター) 開発中に使用するプロファイルを 変更できる
エディター実行時の動作を変える 新機能:プレイモード • エディター実行時に、開発の状況にあわせて、データを どのようにロードするかを変更できる。 • モードによってアセットバンドルやコンテンツカタログ のビルドと更新が必要な場合、プレイモードに入る前に 自動的に処理・更新が行われる。
エディター実行時の動作を変える 新機能:プレイモード Fast Mode: AssetDatabaseからアセットをロードする。 パッキングしない。速いがProfilerで得られる情報が少ない。 Virtual Mode: パッキングせずAssetDatabaseからアセット をロードするが、VirtualAssetBundleを使ってアセットバ ンド ルの動作をシミュレートする。ロードの速度制限機能等、AB利 用時の動作に近い感じで使える。 Packed Mode:実際にパッキングを行い、アセットバ ンドルか らデータをロードする。実機での動作と同じ。
いつ、なにがロードされてアンロードされているのか可視化 新機能:RM Profiler • アセットバ ンドルやアドレス付きアセット のロードとアンロード、参照カウント数を 可視化 • どのような方式でロードされたのかも確 認出来る
RM Profiler
新システムでのワークフローおさらい アドレス グループ プロファイル プレイモード アドレスを使ってロードする アドレス付きアセットは、 プロファイルを追加して、開発 開発中、高速なイテレーション べきアセットを選び、 Addressablesツールの 時、テスト時、リリース時にア が必要なあいだはFast Mode インスペクターで設定するか、 グループ機能を使って、配置し セットのロード場所やアクセス で開発し、アセットのロードを Addressablesツールに たいグループに含める。 するサーバーのURLなどを変 実機に近づけるフェーズになっ 更できるようにする。 てきたら、Virtual Modeや D&Dしてアドレスを設定する。 ロード場所やロード方法が増え アドレス付きアセットを参照す る場合は、新しいグループを追 開発中は、アクティブプロファ るときは、AssetReferenceを 加する。 イルを切り替えて適切な環境 使う。 グループの設定を変更して、各 まとめてロードをしたいアセット グループのビルド場所やロード には、ラベルを設定する。 場所、ロード方法を設定する。 で試していく。 Packed Modeに変更して開 発を続ける。 実行時のリソースのロード・ア ンロードの動作は、RM Profiler を使って確認する。
Addressable Assets の 内部動作を理解する
Addressable Assets と Resource Manager Addressable Assets Resource Manager アセットにアドレスをつけられるようにし、アドレスを使ったア アセットを様々な場所から、様々な方法で、非同期に、ロード・ セットのロードや管理が出来るようにするシステム インスタンス化・管理・アンロードを出来るようにするシステム Resource Managerの上に構築 ユーザーコードで自由に拡張可能 ユーザーコードで自由に拡張可能
2つのシステムの役割分担 Addressable Assets アセットに「アドレス」つけるための機能を提供する アセットの実体とアドレスの対応表(コンテンツカタログ)を 生成する 起動時にグループ設定情報をロードして、各種Providerを 登録し、コンテンツカタログをロードして、Resource Managerを初期化する アドレスでロードする際に、ResourceLocationに変換する Resource Manager ResourceLocationを受け取って、依存関係情報やキャッ シュ情報などを参照しながら適切なAsyncOperationを構 築する リソースの参照カウントを管理し、自動的にリソースの解放を 行う。 プレイモードの Fast Mode や Virtual Mode、RM Profiler などの開発に便利な機能を提供する
Resource Manager & Addressable システム Addressables Resource Locator Resource Location Map ResourceManagerRuntimeData Resource Manager Instance Provider Resource Provider SceneProvider
Resource Managerの仕組み Resource Manager マネージャー。ファサード。 Resource Location リソースの所在地や依存関係情報を提供 Resource Provider リソースのロード・アンロード機能を提供 SceneProvider シーンのロード・アンロード機能を提供 Instance Provider インスタンスの生成・管理機能を提供
Resource Managerがロードを行う流れ 初期化 Resource Provider Resource Manager SceneProvider Instance Provider 各種プロバイダーの登録
Resource Managerがロードを行う流れ リソースの場所 (IResourceLocation) リソースの場所を渡して 場所を担当するResource Providerを ロードを依頼する 探して適切な相手にロードを依頼する Resource Manager ユーザーコード AsyncOperationを返す 受け取ったAsyncOperationに完了時処理を追加して待つ Resource Provider
Resource Managerの依存関係解決 Resource BundledAsset Manager Provider cube.prefab OnCompleted d e p e local.bundle e d n y c n LocalAsset BundleProvider OnCompleted User Code AsyncOperation d e p e cube.bundle e d n y c n RemoteAsset BundleProvider
Addressable System の仕組み Addressables ファサード Resource Locators キーをResource Locationに変換する Resource Location Map キーとResource Locationの変換に参照するデータ ResourceManagerRuntimeData Resource Managerを初期化するための設定
Resource Manager + Addressables Addressablesでの初期化処理 ビルド時にグループ設定から生成 ResourceManagerRuntimeData (settings) JsonAssetProvider Resource Manager TextDataProvider ContentCatalogProvider BundledAssetProvider LocalAssetBundleProvider SceneProvider PooledInstanceProvider RemoteAssetBundleProvider
Resource Manager + Addressables アドレス (文字列や AssetReference) ユーザーコード アドレスをResourceLocation ResourceLocationを Resource Providerを探して に変換、依存関係追記 渡してロードを依頼 適切な相手にロードを依頼 Resource Locator Resource Resource (Contents Catalog) Manager Provider Addressables AsyncOperationを返す 受け取ったAsyncOperationに完了時処理を追加して待つ Resource Manager
ResourceManagerを改造する 独自のリソースのロード方法を実装したい (暗号化、独自バイナリ、独自プロトコル等) Resource Provider 独自のインスタンスの管理方法を実装したい Instance Provider 独自のシーンロード方法を実装したい SceneProvider 独自のアドレス解決方法を実装したい ResoureceLocator
Addressables システムに移行する
Addressable Systemへの移行 Resourcesからの移行
自分のシステムをAddressable Systemに移行する Manifest + AssetBundle Managerからの移行 • 自動的に設定済みアセットバンドルをグループに変換 (ダイアログあり) • アセットバンドルにしているアセットをすべて Addressable Assetsにする • 現在の設定に合わせて、グループとプロファイルを設 定する • ロード処理をAddressables インターフェイスを使って あらたに書き直す
自分のシステムをAddressable Systemに移行する コンテンツカタログが独自形式の場合 • 自分のResourceLocatorを書く • 自分のコンテンツカタログにAssetReferenceから ロケーション変換する機能を追加する • 自分のResource Providerを書く • 独自形式のコンテンツカタログを生成する • 独自形式のコンテンツカタログをロードする
自分のシステムをAddressable Systemに移行する パッキング形式が独自形式の場合 • 自分のResourceProviderを書く • 暗号解除 • 独自バイナリ
自分のシステムをAddressable Systemに移行する ビルド時やプレイモード変更時に自分の処理をフックさせる • コンテンツカタログの生成 • アセットバンドルや独自形式データのビルド
Addressable Systemへの移行 variants • Addressable Systemと新Resource Managerは variantsに非対応 • variantsでやりたかったことは、variantsを使って解決 するより、アドレス解決時に対応する方法をオススメ
THANK YOU 楽しい開発ライフを!