【Unity】AssetBundleが使用しているアセット一覧を渡してlink.xmlを生成する
- 問題:AssetBundleからセットを取得する時にCould not produce class with ID XXXが出る
- 解決方法:コードがストリップされないようにする
- 解決手順:link.xml(ホワイトリスト)を自動生成する
- 補足:Addressableの場合は自動で解決してくれる
- 関連
問題:AssetBundleからセットを取得する時にCould not produce class with ID XXXが出る
AssetBundleを使用している場合、AssetBundleでしか使用していないアセットのコードがビルドからストリップ(削除)されてしまう事があります。特にIL2CPPを利用環境ではよく起こる現象で、iOSやAndroidといったプラットフォームを本番向けにビルドすると起こるかもしれません。
この挙動は特にAssemblyDefenitionFile等でアセンブリが分かれている時に顕著に起こります。ポジティブに言えばAssemblyDefenitionFileを分割すればビルド速度やサイズを削減できると言えますが、悪く言えば非常に簡単にコードがストリップされます。特にUnity 2018から多くのUnity標準機能がpackageに移行しAssemblyDefenitionFileで分割されているので、割と簡単にコードストリップによる動作不全を起こすかもしれません。例えば2D系機能やTimeline等もそれに該当します。
例えば下の画像では「スプライト画像を表示するTimelineを含むPrefab」をAssetBundleに格納し実行時に生成するというコードを実行した結果です。「Could not produce class with ID 212 (SpriteRendererのコードが無いよ!)」のエラーが発生しています。実はコレを解決しても次にTImelineについてのエラーが出ます。
解決方法:コードがストリップされないようにする
この解決法は大雑把に3つです。
- link.xmlでホワイトリストを記述する
- ストリップされないコード(AssemblyDefenitionFile等で分割されていないコード)からクラスを参照する
- ResourcesやScene等から指定のコンポーネントやアセットを参照する
正規ルートは1
です。本来はこちらでやるべきです。 とは言え、正直なところlink.xmlを記述するのは面倒くさいです。ゲームをプレイしてエラーを探す(≒クラッシュするまで色々と試す)は完全に理にかなっていないです。
なので2
と3
は「他のスクリプトや標準で含まれるアセットから参照されているので消されない」という考えを使用して、とりあえずゲームが使用しているPrefab(データを抜いた物)をResourcesやゲームに含めるSceneにでも放り込むという力技も考えられます。面倒なことに頭を悩ませたくない場合の力技として有効な一手です。ただ「気づいたら含まれている」ケースが多いので、link.xmlで記述するのが正規ルートでしょう。
解決手順:link.xml(ホワイトリスト)を自動生成する
link.xmlを自動生成する方法を考えてみます。内容は単純で「AssetBundleに使用しているアセットが使用しているコンポーネント、及び、AssetBundleに格納したデータをlink.xmlに格納する」というものです。ルールは下の通りなので上辺のコンポーネントとデータを網羅しておけば、概ね問題ないという判断です。
手順1:LinkXmlGeneratorの導入
まず下のLinkXmlGenerator
を適当なEditorフォルダ以下に配置します。
AssetBundleNameLinkXMLGenerator.cs · GitHub
手順2:link.xmlを生成
LinkXmlGeneratorでホワイトリストを作ります。覚えておくべきAPIは2つです。 AddAssetsにAssetBundleが使用しているアセットを全部登録して、Saveを実行。あとはlink.xmlをマージするなり、普通にAsset直下に配置すればOKです。
AssetBundlenameを使用せずAssetBundleを生成している場合、このAPIでアセットを登録する感じです。
// アセットが使用しているクラスをホワイトリストに登録する LinkXmlGenerator.AddAssets(string[] assetPaths); // link.xmlをpathに書き出す LinkXmlGenerator.Save(string path);
逆に、AssetBundle nameを使用している場合は下のコードで行けます。
導入後、メニュー>Assets>CreateLink.xml
でファイルを作ってくれます。
using UnityEditor; using UnityEngine; public class AssetBundleChecker { [MenuItem("Assets/CreateLink.xml")] static void Create() { var generator = new LinkXmlGenerator(); // UnityEditor.Animations.AnimatorControllerを取得してしまうので、RuntimeAnimatorControllerに変更 generator.SetTypeConversion(typeof(UnityEditor.Animations.AnimatorController), typeof(RuntimeAnimatorController)); // AssetBundleNameを持つアセットを全て取得して、LinkXmlGeneratorに登録する。 foreach( var bundleName in AssetDatabase.GetAllAssetBundleNames()) { var assetPaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName); generator.AddAssets(assetPaths); } // link.xmlファイルを保存 generator.Save("Assets/link.xml"); AssetDatabase.Refresh(); } }
補足:Addressableの場合は自動で解決してくれる
Addressableの場合は自動でlink.xmlを生成してくれます。
なお以前はlink.xmlをStreamingAssetsに生成して使用していましたが、現在はプロジェクトファイル\Library\com.unity.addressables\StreamingAssetsCopy\aa\プラットフォーム
以下に格納されており、これをゲームビルド時にコピーすることで実現しています。
関連
AssetBundleManifestから使用するコンポーネントを確認するアプローチを取りたい場合は下の記事。コンポーネントが使用しているAssemblyファイルを直接探しに行くスタイルです。