テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】AssetBundleに実際に含まれるアセット一覧を確認する

f:id:tsubaki_t1:20190328095213j:plain

AssetBundleに含めたアセットやシリアライズしたデータ一覧を確認出来る機能を作ってみました。

AssetBundleに自動で含まれるアセット

AssetBundleは、AssetBundle NameやAddressを設定したアセットが参照するアセット…例えばキャラクターにおけるAnimationやMaterialにおけるTexturerを自動的に含みます。これにより開発者側はPrefabを展開する上で必要なアセット群を全て把握する必要なく、Prefabが使えます。

ただし、意図通りではないアセットも含まれる可能性があり、それがAssetBundleの利用面で色々と問題を起こしやすい部分でもあります。

https://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20181224/20181224030957.jpg

AssetBundle.LoadAsset(...) や Manifest に記載されているアセットは、コンテナとして登録されているアセットで、こういった暗黙的に参照され含まれるAssetBundleを確認出来ません。

AssetBundleに含まれるアセットを全て確認する

f:id:tsubaki_t1:20190328093839g:plain

コンテンツの中身を全て確認します。

下のコードをEditorフォルダ以下に格納した後、 AssetBundleを右クリック→AB/Content Checker でABの中身を確認出来ます。

テクスチャの中身等やモデル等も確認が可能です。

using System.Collections.Generic;
using UnityEditor;
using System.Linq;
using UnityEngine;

public class Checker : EditorWindow
{
    List<Object> allObjects = new List<Object>();
    Vector2 pos;

    private void OnGUI()
    {
        using (var scroll = new EditorGUILayout.ScrollViewScope(pos))
        {
            pos = scroll.scrollPosition;
            foreach (var obj in allObjects)
            {
                EditorGUILayout.ObjectField(obj, obj.GetType(), false);
            }
        }
    }

    [MenuItem("Assets/AB/Content Checker")]
    static void Open()
    {
        var window = CreateInstance<Checker>();
        window.ReadContent();
        window.Show();
    }

    void ReadContent()
    {
        AssetBundle bundle = null;
        try
        {
            bundle = AssetBundle.LoadFromFile(Application.dataPath + AssetDatabase.GetAssetPath(Selection.activeObject).Remove(0, 6));

            if (bundle != null)
            {
                SerializedObject so = new SerializedObject(bundle);

                foreach (SerializedProperty content in so.FindProperty("m_PreloadTable"))
                {
                    allObjects.Add(content.objectReferenceValue);
                }

                allObjects = allObjects
                    .Where(c => c != null)
                    .Where(c => c is Component == false )
                    .Where(c => c is GameObject == false)
                    .Distinct().OrderBy(c => c.name).ToList();
                bundle.Unload(false);
            }
            else
            {
                Debug.LogWarning("this is not assetbundle?");
                Close();
            }
        }
        finally
        {
            if( bundle != null)
                bundle.Unload(false);
        }
    }
}

例えば下のように、アドレスとしてはPrefabとAnimatorControllerのみを設定しています。

これをチェッカーで見ると、Prefabが暗黙的に参照している内で、同じAssetBundleに格納されている全てのアセットを確認出来ます。

f:id:tsubaki_t1:20190328095830j:plain

またSprite Atlasでの動き等もココで確認しやすいです。下の画像を見ると、Sprite AtlasとAssetBundleを組み合わせる場合、SpriteAtlasに登録したAssetBundleを全て同じAssetBundleに登録している場合、含まれるアセットはパック済みTextureのみである事が確認出来ます。

f:id:tsubaki_t1:20190328100839j:plain

現状の問題

  • シェーダーが上手く取れません。

  • 他のAssetBundleをLoadしていると、開いているAssetBundleに含まれるアセットも表示されるかもしれません。

CSVで出力もついてでに作ってみる

複数のAssetBundleの中身を一気に比較する用でCSV出力もつけてみました。

f:id:tsubaki_t1:20190328102218g:plain
Default-SpriteがAssetBundleに複製格納されているせいで、バッチが破壊されるの図

using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;
using System.Linq;

public class ExportCSV : MonoBehaviour
{
    [MenuItem("Assets/AB/ExportCSV")]
    static void Export()
    {
        var valuePairs = new Dictionary<string, string>();
        StringBuilder builder = new StringBuilder();

        foreach (var t in Selection.objects)
        {
            AssetBundle bundle = null;
            try
            {
                bundle = AssetBundle.LoadFromFile(Application.dataPath + AssetDatabase.GetAssetPath(t).Remove(0, 6));
                if (bundle != null)
                {
                    builder.Append($"{bundle.name}, ");
                    SerializedObject so = new SerializedObject(bundle);

                    List<Object> objs = new List<Object>();
                    foreach (SerializedProperty content in so.FindProperty("m_PreloadTable"))
                    {
                        objs.Add(content.objectReferenceValue);
                    }

                    objs = objs
                       .Where(c => c != null)
                       .Where(c => c is Component == false)
                       .Distinct().OrderBy(c => c.name).ToList();

                    foreach (var obj in objs)
                    {
                        builder.Append($"{obj.name}({obj.GetType()}), ");
                    }
                    builder.AppendLine();
                }
            }
            finally
            {
                if (bundle != null) bundle.Unload(true);
            }
        }

        System.IO.File.WriteAllText("ab content.csv", builder.ToString());
    }
}

感想

エディター基本機能を少し拡張しましたが、案外悪くないかも? ABに含まれるテクスチャの確認が楽で良いです。

それとFBXにAssetBundle Nameを含めることでどんだけ無駄なゴミが増えるのかを確認出来ます。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp