Skip to main content
そろそろ楽がしたい!
新アセットバンドルワークフロー &
リソースマネージャー詳細解説
大前 広樹
ユニティ・テクノロジーズ・ジャパン
2018/05/09DAY3
大前 広樹
Unityで働いている。 日本リージョンの指揮官役の他、
アセットバンドルまわりのツール開発なども助ける。
ユニティ・テクノロジーズ・ジャパン
@pigeon6 hiroki-o hiroki@unity3d.com
Addressable Assets
Addressable Assets が解決したい問題
• アプリのダウンロードサイズを小さくしたい
• アプリ配信後にコンテンツを動的に配信・更新したい
• ゲームのファイル構成を、パッチを当てやすい形にしたい
• 正直めんどいので楽がしたい
Addressable Assets の良さみ
• Resourcesのシンプルさとアセットバンドルの自由度を実現
• ロードの依存関係や非同期処理を、複雑なコードを書かずに実現
• (Scriptable Build Pipelineで) ビルドやプレイモード時の動作もカスタマイズ可能に
• 開発を助けるツール類の提供
• C#
Addressable Assets システム のステータス
• 現在はプレビュー版
• Unity 2018.2から利用可能
• Package Managerでインストール
• 今すぐさわってみるためには

manifest.jsonの書き換えが必要
{
"dependencies": {
"com.unity.addressables": "0.0.22-preview"
}
}
Addressable Assets と Resourcesの違い
共通点
違い

(進化点)
• 固定の文字列(アドレス・パス)を使ったロードができる
• アセットの明示的なロードやアンロードができる
• 非同期でのロード処理ができる
• プレイモード開始前に自動で必要な更新が行われる
• アセットの実際の配置を自由に変更できる
• 自由な文字列をアドレスとしてつけられる
• ラベルやAssetReferenceが使える
• ロード方法やビルド方法などをカスタマイズできる
アドレスを使ったシステムへの疑問
ハードコード・・・? インスペクター・・・?
アドレスを使って開発しつつも、作りやすさを変えない
• AssetReferenceを使うことで、アセット
のアドレスをPrefabなどを指定するのと
同じような感覚で作ることができる
• アーティストやゲームデザイナーにも

優しいインターフェイス
• アドレス自体の打ち間違い防止
• 変更に強い
新機能:AssetReference
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 メソッドを使う
新機能:参照カウント
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 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);
… … …
}
}
Addressablesから
インスタンス化したら・・・
Addressablesからリリースする
アドレスの設定
Address チェックボックスで

アドレスを追加
アドレスの設定
AssetReferenceに D&D
アドレスが自動で付与
Addressables ツール
ドラッグ&ドロップで、アセットを
Adressable化&グループ登録
アドレスやラベルを変更
パッキングとロードの方法を決める
• アセットの配置場所やパッキング方法、ロード方法
などをグループごとに設定できる
• アセットが所属するグループを変えても、

コードは一切変更の必要がない
新機能:グループ
パッキングとロードの方法を決める
アセットのまとめ方(単一vs個別)
ビルド時に配置する場所
ロード時に使うパス
グループ設定インターフェイス
パッキングとロードの方法を決める
ローカル: StreamingAssetsに配置した

アセットバンドルからロードする。
リモート: 任意のサーバーからUnityWebRequest
でロードする。
アドバンスド: 任意の場所から任意のロード方法で

ロードする。
利用可能なグループ
アドレスとアセットの対応
新機能:コンテンツカタログ
• アドレス、ラベル、グループの情報を記録するデータ
• json形式で作成
• ビルド時やプレイモード変更時に自動生成
• リモートのグループ利用時には、リモートにも配置可能
• グループ単位ではなく、グローバルなカタログを生成
• hashによる更新検知
• アセットの依存関係もコンテンツカタログに記載
複数のリソースをまとめて扱う
• 「このステージを起動する前にあらかじめロードしておく
べきリソースを、事前にロードしたい」
• アドレス付きアセットには、ラベルを設定することが

できる
• 一つのアドレスに、任意のラベルを複数つけることが

できる
• ラベルが該当するアセットを事前に一括ロード
新機能:ラベル
List<object> assets = new List<object>();
var op = Addressables.PreloadDependencies("MyLabel", null);
op.Completed += (res) =>
{
assets.AddRange(res.Result);
};
yield return op;
事前にアセットを先読みするサンプルコード
“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;
事前にアセットを先読みするサンプルコード
ロードが完了するまで待つ
開発中とリリース時で異なる設定を使う
新機能:プロファイル
開発中とリリース時で異なる設定を使う
• ロード場所やビルド場所を、開発中やテスト中、リリース時などの用途に合わせて設定
• [BuildTarget] や {UnityEngine.Application.streamingAssetsPath} などの

変数が利用可能
プロファイルの用途
開発中とリリース時で異なる設定を使う
アクティブプロファイルの切替(エディター)
開発中に使用するプロファイルを
変更できる
エディター実行時の動作を変える
• エディター実行時に、開発の状況にあわせて、データを
どのようにロードするかを変更できる。
• モードによってアセットバンドルやコンテンツカタログ
のビルドと更新が必要な場合、プレイモードに入る前に
自動的に処理・更新が行われる。
新機能:プレイモード
エディター実行時の動作を変える
新機能:プレイモード
Fast Mode: AssetDatabaseからアセットをロードする。

パッキングしない。速いがProfilerで得られる情報が少ない。
Virtual Mode: パッキングせずAssetDatabaseからアセット
をロードするが、VirtualAssetBundleを使ってアセットバンド
ルの動作をシミュレートする。ロードの速度制限機能等、AB利
用時の動作に近い感じで使える。
Packed Mode:実際にパッキングを行い、アセットバンドルか
らデータをロードする。実機での動作と同じ。
いつ、なにがロードされてアンロードされているのか可視化
• アセットバンドルやアドレス付きアセット
のロードとアンロード、参照カウント数を
可視化
• どのような方式でロードされたのかも確
認出来る
新機能:RM Profiler
RM Profiler
プレイモードプロファイルグループ
新システムでのワークフローおさらい
アドレス
アドレス付きアセットは、
Addressablesツールの

グループ機能を使って、配置し
たいグループに含める。
ロード場所やロード方法が増え
る場合は、新しいグループを追
加する。
グループの設定を変更して、各
グループのビルド場所やロード
場所、ロード方法を設定する。
プロファイルを追加して、開発
時、テスト時、リリース時にア
セットのロード場所やアクセス
するサーバーのURLなどを変
更できるようにする。
開発中は、アクティブプロファ
イルを切り替えて適切な環境
で試していく。
アドレスを使ってロードする

べきアセットを選び、

インスペクターで設定するか、
Addressablesツールに

D&Dしてアドレスを設定する。
アドレス付きアセットを参照す
るときは、AssetReferenceを
使う。
まとめてロードをしたいアセット
には、ラベルを設定する。
開発中、高速なイテレーション
が必要なあいだはFast Mode
で開発し、アセットのロードを
実機に近づけるフェーズになっ
てきたら、Virtual Modeや
Packed Modeに変更して開
発を続ける。
実行時のリソースのロード・ア
ンロードの動作は、RM
Profiler を使って確認する。
Addressable Assets の
内部動作を理解する
Addressable Assets と Resource Manager
Addressable Assets Resource Manager
アセットにアドレスをつけられるようにし、アドレスを使ったア
セットのロードや管理が出来るようにするシステム
Resource Managerの上に構築
ユーザーコードで自由に拡張可能
アセットを様々な場所から、様々な方法で、非同期に、ロード・

インスタンス化・管理・アンロードを出来るようにするシステム
ユーザーコードで自由に拡張可能
Addressable Assets Resource Manager
2つのシステムの役割分担
ResourceLocationを受け取って、依存関係情報やキャッ
シュ情報などを参照しながら適切なAsyncOperationを構
築する
リソースの参照カウントを管理し、自動的にリソースの解放を
行う。
プレイモードの Fast Mode や Virtual Mode、RM Profiler
などの開発に便利な機能を提供する
アセットに「アドレス」つけるための機能を提供する
アセットの実体とアドレスの対応表(コンテンツカタログ)を
生成する
起動時にグループ設定情報をロードして、各種Providerを
登録し、コンテンツカタログをロードして、Resource
Managerを初期化する
アドレスでロードする際に、ResourceLocationに変換する
Resource Manager & Addressable システム
Addressables
Resource Manager
Resource Locator
Resource Provider
ResourceManagerRuntimeDataResource Location Map
SceneProviderInstance Provider
Resource Managerの仕組み
Resource Manager
Resource Provider
Resource Location
SceneProvider
Instance Provider
マネージャー。ファサード。
リソースのロード・アンロード機能を提供
リソースの所在地や依存関係情報を提供
シーンのロード・アンロード機能を提供
インスタンスの生成・管理機能を提供
Resource Managerがロードを行う流れ
Resource Manager
Resource Provider
SceneProvider
Instance Provider
初期化
各種プロバイダーの登録
Resource Managerがロードを行う流れ
ユーザーコード
リソースの場所
(IResourceLocation)
Resource Manager
リソースの場所を渡して
ロードを依頼する
Resource Provider
場所を担当するResource Providerを
探して適切な相手にロードを依頼する
受け取ったAsyncOperationに完了時処理を追加して待つ
AsyncOperationを返す
Resource Managerの依存関係解決
Resource
Manager
User Code
OnCompleted OnCompleted
RemoteAsset

BundleProvider
dependency
cube.bundle
BundledAsset
Providercube.prefab
LocalAsset

BundleProvider
dependency
local.bundle
AsyncOperation
Addressable System の仕組み
ファサード
キーとResource Locationの変換に参照するデータ
キーをResource Locationに変換する
Resource Managerを初期化するための設定
Addressables
Resource Locators
ResourceManagerRuntimeData
Resource Location Map
Resource Manager + Addressables
Resource Manager
Addressablesでの初期化処理
TextDataProvider
JsonAssetProvider
ContentCatalogProvider
BundledAssetProvider
LocalAssetBundleProvider
RemoteAssetBundleProvider
ResourceManagerRuntimeData
(settings)
SceneProvider PooledInstanceProvider
ビルド時にグループ設定から生成
Resource Locator

(Contents Catalog)
Resource Manager + Addressables
Resource
Manager
Resource
Provider
ユーザーコード
アドレス

(文字列や
AssetReference)
ResourceLocationを
渡してロードを依頼
Resource Providerを探して
適切な相手にロードを依頼
受け取ったAsyncOperationに完了時処理を追加して待つ
AsyncOperationを返す
アドレスをResourceLocation
に変換、依存関係追記
Addressables Resource Manager
ResourceManagerを改造する
Resource Provider
SceneProvider
Instance Provider
ResoureceLocator
独自のリソースのロード方法を実装したい
(暗号化、独自バイナリ、独自プロトコル等)
独自のインスタンスの管理方法を実装したい
独自のシーンロード方法を実装したい
独自のアドレス解決方法を実装したい
Addressables システムに移行する
Addressable Systemへの移行
Resourcesからの移行
自分のシステムをAddressable Systemに移行する
• 自動的に設定済みアセットバンドルをグループに変換
(ダイアログあり)
• アセットバンドルにしているアセットをすべて
Addressable Assetsにする
• 現在の設定に合わせて、グループとプロファイルを設
定する
• ロード処理をAddressables インターフェイスを使って
あらたに書き直す
Manifest + AssetBundle Managerからの移行
自分のシステムをAddressable Systemに移行する
• 自分のResourceLocatorを書く
• 自分のコンテンツカタログにAssetReferenceから

ロケーション変換する機能を追加する
• 自分のResource Providerを書く
• 独自形式のコンテンツカタログを生成する
• 独自形式のコンテンツカタログをロードする
コンテンツカタログが独自形式の場合
自分のシステムをAddressable Systemに移行する
• 自分のResourceProviderを書く
• 暗号解除
• 独自バイナリ
パッキング形式が独自形式の場合
自分のシステムをAddressable Systemに移行する
• コンテンツカタログの生成
• アセットバンドルや独自形式データのビルド
ビルド時やプレイモード変更時に自分の処理をフックさせる
Addressable Systemへの移行
• Addressable Systemと新Resource Managerは
variantsに非対応
• variantsでやりたかったことは、variantsを使って解決
するより、アドレス解決時に対応する方法をオススメ
variants
楽しい開発ライフを!
THANK YOU