ParticleSystemを含んだPrefabをAssetBundleにした際に困った話

AvatarPosted by

この記事はGRIPHONE Advent Calendar 2020 8日目の記事です。

こんにちは。Unityエンジニアの上岡です。
今回は、ParticleSystemを含んだPrefabをAssetBundleにした際に少し困った話をします。

AssetBundleのビルドに関わるエンジニアの人は
依存関係がー、とか、サイズがー、とか色々苦労しているのではないかと思います。
もしかしたら今回の記事は、サイズの問題を多少解決できるかもしれない可能性があります。

これは、Unity2018.4.18f1での話です。
もしかしたら新しいUnityでは起こらない問題かもしれませんので
そこはご了承ください。

AssetBundleによくわからない依存関係が生まれた

ある日、AssetBundleの依存関係が何かおかしいと相談を受けました。
必要ないデータを参照してしまって何かおかしくなっているらしい、と。

とりあえず、中身を確認しようと
AssetBundle Browserでみてみました。
(AssetBundle Browserは、AssetBundleのビルドができたり、中身が見れたりと色々と便利です。
 Package Managerから入れることが可能です)

(これは記事用に作ったファイルなんですが、赤で括ったものが必要ないものだと思ってください)

確かに何か不要なものが存在している模様。

まずprefabからBarrelのMeshを探してみました。
ParticleSystemの設定を一通り見直しても見つからなかったのですが
そんなところに残ってるの?といった感じのところにいました。

Rendererの設定です。
一見問題はないようにみえますね。
では、Render ModeをMeshにしてみましょう。

いましたね・・・。

ついでなので、add_color_01_2とadd_color_01_3の参照は

ここにいました。
Texture Sheet Animationは未使用なんですが・・・。

諸々未使用な参照を全部削除したあとに、再ビルドしたAssetBundleの中身がこちらになります。

きれいになりましたね。

どうやら

Module自体が未使用だったり、参照しているファイルが実際には未使用でもAssetBundleには内包されてしまうようです。
まぁ、参照が残ってるんだから入るよね、と言われてしまえばそうなんですが・・・。

このようなデータが出来てしまう経緯は
実際にデータを作っている方が、ほかのデータを複製してそれをベースにして作った場合だと思っています。
新規ファイルとして一から作っている場合は起こらないはずです。

とは言ったものの、デザイナーの方に複製禁止して「全部一から作ってください!」
と言うのもさすがにひどいと思われるので、エディタ拡張で無駄な参照だけ消去するものを用意しました。

不要参照を消すコードはこんな感じになります。

foreach (var g in Selection.objects.OfType<GameObject>())
{
    var particleSystems = g.GetComponentsInChildren<ParticleSystem>();
    foreach (var particleSystem in particleSystems)
    {
        if (particleSystem.textureSheetAnimation.enabled == false || 
            particleSystem.textureSheetAnimation.mode != ParticleSystemAnimationMode.Sprites)
        {
            if (particleSystem.textureSheetAnimation.spriteCount > 0)
            {
                particleSystem.textureSheetAnimation.SetSprite(0, null);
                for (var i = 1; i < particleSystem.textureSheetAnimation.spriteCount; i++)
                {
                    particleSystem.textureSheetAnimation.RemoveSprite(i);
                }
            }
        }

        var renderer = particleSystem.GetComponent<ParticleSystemRenderer>();
        if (renderer)
        {
            if (renderer.renderMode != ParticleSystemRenderMode.Mesh)
            {
                renderer.mesh = null;
            }
            if (renderer.enabled == false)
            {
                renderer.material = null;
                renderer.trailMaterial = null;
                renderer.mesh = null;
            }                        
        }
    }

    PrefabUtility.SavePrefabAsset(g);
}

不要な参照を消して、Prefabを上書きしているだけです。
実際には、RendererとTexture Sheet Animation以外にも同じ問題が起こる箇所があります。(shapeとか)
どこまで対応するかはチームの方針や担当者次第かな、と思います。
自分的には、Meshはそこまで大きいものはないと思うのでそこまで気にしなくてもいいですが
Textureはサイズが大きいものが参照されているかもしれないので気を付けたほうが良いかなと思っています。

おわりに

というわけで、AssetBundleに未使用データが入ってしまうお話でした。

プロダクト次第では、まったく効果がなかったりすごく効果があったりするかな、と思います。
(運用が長いプロダクトなら大きな効果があったりするかもしれません)

参考までに、現在運用中の弊社のプロダクトのエフェクトデータに上記処理をかけてみたところ
AssetBundleの総サイズが
16.5 MB (17,342,268 バイト)→15.0 MB (15,794,103 バイト)
となりました。(1.5Mほどゴミがあったということですね・・・)

これが大きいか小さいかは何とも言えませんが
AssetBundleのサイズ問題で悩んでいる方がいたら、その一助になれたら幸いです。