問題: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についてのエラーが出ます。
AssetBundleでしか使用していないコンポーネント を使用するとよく見る風景
解決方法:コードがストリップされないようにする
この解決法は大雑把に3つです。
link.xml でホワイトリスト を記述する
ストリップされないコード(AssemblyDefenitionFile等で分割されていないコード)からクラスを参照する
ResourcesやScene等から指定のコンポーネント やアセットを参照する
正規ルート は1
です。本来はこちらでやるべきです。 とは言え、正直なところlink.xml を記述するのは面倒くさいです。ゲームをプレイしてエラーを探す(≒クラッシュするまで色々と試す)は完全に理にかなっていないです。
ゲームが使用しているコンポーネント をホワイトリスト 一覧に記述
なので2
と3
は「他のスクリプト や標準で含まれるアセットから参照されているので消されない」という考えを使用して、とりあえずゲームが使用しているPrefab(データを抜いた物)をResourcesやゲームに含めるSceneにでも放り込むという力技も考えられます。面倒なことに頭を悩ませたくない場合の力技として有効な一手です。ただ「気づいたら含まれている」ケースが多いので、link.xml で記述するのが正規ルート でしょう。
Resourcesやゲームに含めるSceneに入れるでもホワイトリスト 入りはする
link.xml を自動生成する方法を考えてみます。内容は単純で「AssetBundleに使用しているアセットが使用しているコンポーネント 、及び、AssetBundleに格納したデータをlink.xml に格納する」というものです。ルールは下の通りなので上辺のコンポーネント とデータを網羅しておけば、概ね問題ないという判断です。
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);
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();
generator.SetTypeConversion(typeof (UnityEditor.Animations.AnimatorController), typeof (RuntimeAnimatorController));
foreach ( var bundleName in AssetDatabase.GetAllAssetBundleNames())
{
var assetPaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
generator.AddAssets(assetPaths);
}
generator.Save("Assets/link.xml" );
AssetDatabase.Refresh();
}
}
補足:Addressableの場合は自動で解決してくれる
Addressableの場合は自動でlink.xml を生成してくれます。
tsubakit1.hateblo.jp
なお以前はlink.xml をStreamingAssetsに生成して使用していましたが、現在はプロジェクトファイル\Library\com.unity.addressables\StreamingAssetsCopy\aa\プラットフォーム
以下に格納されており、これをゲームビルド時にコピーすることで実現しています。
関連
AssetBundleManifestから使用するコンポーネント を確認するアプローチを取りたい場合は下の記事。コンポーネント が使用しているAssemblyファイルを直接探しに行くスタイルです。
tsubakit1.hateblo.jp