AssetBundle management and use.
Autoyaでは、AssetBundleの生成から、使用、更新、分割管理までをサポートしている。
Supported
- AssetBundle(AB)のロード/使用
- ABをAssetBundleList(リスト)単位で運用、更新
- AB、リストの生成
- 複数のリストの生成と使用
- 使用予定のABをまとめて事前に取得
- ABの更新
機構の前提
機構の状態設定について
AB関連の機構には独立した状態設定がある。
- ABList未取得状態
- ABList取得済み状態
端末内に最低一つのAssetBundleListを保持すると、自動的に取得済み状態に変化する。
ABList取得済み状態では、ABからAssetを取り出したりABをPreloadすることができる。
もしAppでAssetBundleを使うのが確定している場合、App起動時の適当なタイミングで AssetBundle_DownloadAssetBundleListsIfNeed メソッドを実行すると、Appに必要なリストを自動的に取得してくる。
URLを独自に指定して手動でリストを取得するには、 AssetBundle_DownloadAssetBundleListFromUrlManually が使える。
どちらにせよ、取得するリストについて、リストの設定がなされている必要がある。
リスト取得のフロー
AssetBundle_DownloadAssetBundleListsIfNeed 関数では、図の様なフローを通過する。
複数のOverridePointsを経由するため、取得したデータの扱いを変更したい場合はOverridePointsの各関数を編集するとよい。
AssetBundle機構の状態については、 bool AssetBundle_IsAssetBundleFeatureReady メソッドで確認することが可能になっている。
リストの設定について
起動してAssetBundle機構を使う(= AssetBundle_DownloadAssetBundleListsIfNeed メソッドを実行する)タイミングで、もしリストが端末内になければ所得しに行く。
サンプルAppでは、RuntimeManifest(AutoyaRuntimeManifestObject.cs)に[main_assets, sub_assets]という2つのAssetBundleListを保持していて、これらの情報を元にリストを取得するurlを、OverridePoints.csの string OnAssetBundleListDownloadUrlRequired(string listIdentity) 関数で生成する、という形になっている。
public RuntimeManifestObject()
{
resourceInfos = new AssetBundleListInfo[]{
new AssetBundleListInfo
{
listIdentity = "main_assets",
listVersion = "1.0.0",
listDownloadUrl = "https://raw.githubusercontent.com/sassembla/Autoya/assetbundle_multi_list_support/AssetBundles/"
},
new AssetBundleListInfo
{
listIdentity = "sub_assets",
listVersion = "1.0.0",
listDownloadUrl = "https://raw.githubusercontent.com/sassembla/Autoya/assetbundle_multi_list_support/AssetBundles/"
}
};
}
現状のAutoyaでは、事前にRuntimeManifestにリストのIdentityなどの情報が入っていないと、そのリストを取得したりアップデートしたりといったことができない。 これは今後のバージョンで改善される可能性がある。
AssetBundleからAssetのロード/使用
Asset名とそのAssetの型をもとに、ABからAssetを取得する。
Autoya.AssetBundle_LoadAsset<GameObject>(
assetName,
(name, obj) =>
{
// 展開成功、objはGameObject型
},
(name, err, reason, autoyaStatus) =>
{
// 失敗
}
);
どのAssetBundleにどのAssetが含まれているかなどの情報はAssetBundleListが保持しているため、それらの情報を気にする必要がない。
on demand download
特定のABに含まれるAssetをリクエストした際、そのABがダウンロード前であれば、ABをDLした後でAssetを展開する。
あるAssetを使う際、Assetを含むABがDL済みでなければ、DL後に展開される。(依存の解決)
Asset in AB[not Cached] -> ABをDL -> Asset展開
一度DLされたABは端末にキャッシュされる。
Assetを含んでいるABがすでにキャッシュ済みな場合、DL処理なしで展開される。
Asset in AB[Cached] -> Asset展開
AssetBundleはどこからダウンロードされるのか
使用したいAssetを含んだABがまだキャッシュされていない場合、この機構は特定のパスを使ってAssetBundleのDLを開始、完了次第Assetの取り出しを行う。
AssetBundleのDLを行う際、OverridePoints/OnAssetBundleDownloadUrlRequired メソッドを通る。
private string OnAssetBundleDownloadUrlRequired(string listIdentity)
{
var targetListInfo = Autoya.Manifest_LoadRuntimeManifest().resourceInfos.Where(info => info.listIdentity == listIdentity).FirstOrDefault();
if (targetListInfo == null)
{
throw new Exception("failed to detect bundle info from runtime manifest. requested listIdentity:" + listIdentity + " is not contained in runtime manifest.");
}
var url = targetListInfo.listDownloadUrl + "/" + targetListInfo.listIdentity + "/" + AssetBundlesSettings.PLATFORM_STR + "/" + targetListInfo.listVersion + "/";
return url;
}
デフォルトではこのメソッド内で、AssetBundleをDownloadする際のurlを決定している。
- そのAssetBundleが含まれているAssetBundleListのidentityをキーに、RuntimeManifestからAssetBundleListの情報を取得
- listDownloadUrlをbasePathとして扱い、
- listIdentityを追加
- platform文字列を追加
- リストのバージョン値を追加し、urlは完成となる。
最終的にここで生成されたURLは次のようなものになる。
return "そのABが所属しているAssetBundleListのダウンロード用basePath/プラットフォーム文字列/リストバージョン値/";
リストとそのリストに含まれるAssetBundleファイルを同じフォルダに入れた状態でCDNへとアップする、というのをサンプル版の前提としているため、変更するためにはこの関数で返すパス情報を変えればよい。
(この部分はちょっと切り分けがうまくいっていないので、AssetBundleのファイル名までを足させる方向に変わるかもしれない。)
AssetBundleの状態について
各ABは、機構の内部で3段階の状態を持っている。
NotCached <-> OnStorage <-> OnMemory(AssetBundleLoaded)
- NotCached: 端末にABがない
- OnStorate: ABキャッシュがあるが未展開
- OnMemory: ABキャッシュがメモリに展開済み
Assetを取得する際、含有するABの状態が変化し、OnMemoryになったABからAssetが生成される。
機構内部のABの状態を気にする必要はないが、知っておくとメモリ状態を把握することが可能。
なおメモリ上に展開したABは、 AssetBundle_UnloadOnMemoryAssetBundles などのAPIで手動で解放する。
リストの内容とその生成について
リスト(AssetBundleList)はjson形式のファイルで、AutoyaではAssetBundleList型として実装されている。
内容は次の通り。
list
└ identity // identity of this list.
└ target // human readable target platform name.
└ version // human readable version desc.
└ assetBundles // assetBundleInfo[]
└ assetBundleInfo
└ bundleName // bundle name.
└ assetNames // contained asset names. e,g, "Assets/Somewhere/texture.png"
└ dependsBundleNames // the bundle names which this assetBundle depends on.
└ crc // crc parameter. used for crc check.
└ hash // hash parameter. used for exchange same asset from old one to new one.
└ size // size of uncompressed AssetBundle.
Autoyaでは、このリストをApp内に保持、必要に応じて更新することで、AssetBundleとそれに含まれるAssetを簡単に扱うことができるようになっている。
リストのアップデート
サーバ主導でAppが使うべきリストを更新することができる。
Update Resource version を参照。
リストとリソースの生成については、リソースの生成を参照。
ABを複数のリストに分ける
Autoyaでは複数のリストに分けてのAssetBundleの生成と管理、更新をサポートしている。
Update Resource version を参照。
preload AssetBundle
複数のABを一括でDL、キャッシュする機構。この機構でDLしたABはメモリに展開されず、キャッシュされるのみになる。 主にon demandでAssetをDLさせたくない、Asset使用前に部分的にDLを済ませたい、という場合に便利。
さらに並列DLが可能になっており、複数のABをできるだけ高速にDLすることができる。
取得済みであっても、[リストが更新 + 含まれているABにも更新有り]という状態のABを最新版へと更新する効果もある。 なお、キャッシュ済みかつ更新がないABは当然変化しない。
preload機能では、DLしたいABの情報をPreloadList型のインスタンスで渡してロードを実行する。
次のようなPreloadList型のデータから複数のABを纏めてDLする機能を提供する。
preloadList
└ name // human readable name of list.
└ bundleNames // string[]
└ bundleName // preload target bundle name.
この型をパラメータ入りでnewして、App内でPreload機構を使用する。
例えばsamplePreloadListという名前のPreloadListを作成、preloadTargetBundleName1という名前のABをPreloadしたい場合、
次のようなコードを書くと叶う。
var newPreloadList = new PreloadList("samplePreloadList", new string["preloadTargetBundleName1"]);
Autoya.AssetBundle_PreloadByList(
newPreloadList,
(willLoadBundleNames, proceed, cancel) =>
{
proceed();
},
progress =>
{
Debug.Log("progress:" + progress);
},
() =>
{
Debug.Log("preloading all listed assetBundles is finished.");
},
(code, reason, autoyaStatus) =>
{
Debug.LogError("preload failed. code:" + code + " reason:" + reason);
},
(downloadFailedAssetBundleName, code, reason, autoyaStatus) =>
{
Debug.LogError("failed to preload assetBundle:" + downloadFailedAssetBundleName + ". code:" + code + " reason:" + reason);
},
10 // 10 parallel download! you can set more than 0.
);
HTTPを介してサーバ側からPreload Listを取得、適宜必要なABをpreloadさせる運用も可能。
Autoya.AssetBundle_Preload(
"https://somewhere/preloadlist.json",
(willLoadBundleNames, proceed, cancel) =>
{
var totalWeight = Autoya.AssetBundle_GetAssetBundlesWeight(willLoadBundleNames);
Debug.Log("start downloading bundles. total weight:" + totalWeight);
proceed();
},
progress =>
{
Debug.Log("progress:" + progress);
},
() =>
{
Debug.Log("preloading 1 listed assetBundles is finished.");
},
(code, reason, autoyaStatus) =>
{
Debug.LogError("preload failed. code:" + code + " reason:" + reason);
},
(downloadFailedAssetBundleName, code, reason, autoyaStatus) =>
{
Debug.LogError("failed to preload assetBundle:" + downloadFailedAssetBundleName + ". code:" + code + " reason:" + reason);
},
10 // 10 parallel download! you can set more than 0.
);
このスタイルは特に強力で、例えば特定の画面に遷移する際に実行すると、
「その画面以降で使うAssetとしてどんなものをDLしておくべきか」を常にサーバに問い合わせることができる様になる。
これにより、「近い未来に使用するAssetを事前にダウンロードさせる」ということが可能になる。
Preload系のAPIの第二引数 onBeforePreload引数について
この引数には、[PreloadListの取得に成功し、Preloadする内容が完全に判明した段階で、Preload処理を継続するかキャンセルするか] という判断を盛り込むことができる。
Autoya.AssetBundle_Preload(
"https://somewhere/preloadlist.json",
(willLoadBundleNames, proceed, cancel) =>
{
var totalWeight = Autoya.AssetBundle_GetAssetBundlesWeight(willLoadBundleNames);
Debug.Log("start downloading bundles. total weight:" + totalWeight);
proceed();
},...
willLoadBundleNames には未DLのABの名前が来る。すでにDLが済んでいるABの名前は含まれない。 Preloadを継続する場合はproceed関数を実行、中断する場合はcancel関数を実行する。 これからDLするABの名前一覧が取得できているので、これからDLするABの総重量をDL前に計算し、例えばプレイヤーに提示することができる。
「このくらいの容量のデータをDLします、よろしいですか?」とか表示して、yesであればproceed関数を実行する。非同期で継続実行が可能になっている。
内部フロー
次の図の様なフローを通過する。
OverridePoints/OnAssetBundlePreloadListGetRequest 関数が着火される。この関数上でリクエストヘッダのパラメータを調整することができる。
- 実際に総重量を取得するメソッドはAPIを見てね AssetBundles
- 設定ファイルの話
Factory Reset
AssetBundle系のファクトリーリセットとして、ABやリストを削除してインストール状態時への復帰を行うメソッドがある。 AssetBundle_FactoryReset(Action succeeded, Action<> failed)
このメソッドが成功すると、AppのAssetBundleはすべてUnloadされ、キャッシュから削除される。また、AssetBundleListもAppのインストール時の状態に戻る。
そのため、AssetBundleListの再取得、最新のリストの再取得を行わせることができる。 なにかしらABの配信に失敗した時に、プレイヤーが自発的に実行できるようにしておくといいかもしれない。