【Unity】cgincにはAssetBundle名を付けないと更新が反映されない話

AssetBundleから参照されるcgincファイルにはAssetBundle名をつけないとcgincの更新が反映されない現象と対応方法です。

Unity2018.2

2019/2/20追記

Unity2018.3.1で直っているかも?というコメントをいただいたので試してみました。
手元にあったUnity2018.3.6で試してみたところ・・・確かに修正されていました!

  • MaterialにだけAssetBundle名を付けてcgincやShaderにはAssetBundle名を付けなかった場合
  • ShaderにだけAssetBundle名を付けてcgincやMaterialにはAssetBundle名を付けなかった場合

の両方とも正常にAssetBundleがビルドされることを確認しました。

情報ありがとうございます!

準備

まず検証用のcgincファイルを作ります。
色を適当に返すGetColor()という関数だけが定義されています。

#ifndef EXAMPLE_INCLUDED
#define EXAMPLE_INCLUDED

inline float4 GetColor(){ return float4(1, 0, 0, 1); }

#endif

つぎにこれをシェーダから読み込みます。
上記のcgincのGetColor()の結果を返すだけのシェーダです。

Shader "Unlit/shader_assetbundle_example"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
            
           #include "UnityCG.cginc"
           #include "cginc_assetbundle_example.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                return GetColor();
            }
            ENDCG
        }
    }
}

さらにこれをMaterialにセットします。
ここまでで3つのファイルができました。

f:id:halya_11:20181230221328p:plain

問題点

前節のファイルのうちMaterialだけをAssetBundleにします。
すると当然、MaterialのほかにShaderやテクスチャが含まれたAssetBundleが出来上がります。

f:id:halya_11:20181230221613p:plain

ここで一度このAssetBundleをビルドして、AssetBundleからMaterialを読み込みます。
ロードはこんな感じで適当に。

using UnityEngine;

public class AssetBundleExample : MonoBehaviour {

    [SerializeField]
    private string _assetBundlePath;
    [SerializeField]
    private string _assetPath;

    private void Start()
    {
        var assetBundle = AssetBundle.LoadFromFile(_assetBundlePath);
        var material = assetBundle.LoadAsset<Material>(_assetPath);
        GetComponent<MeshRenderer>().material = material;
        assetBundle.Unload(false);
    }
}

結果は正常にcgincで定義した色が反映されます。

f:id:halya_11:20181230222254p:plain

問題はここからです。
この状態でcgincのGetColor()で定義した色だけ変えてみます。

// 黄色に変更
inline float4 GetColor(){ return float4(1, 1, 0, 1); }

次にAssetBundleを差分ビルド(既存のAssetBundleを消さずにビルド)します。
そしてまた上記と同様にMaterialを読み込みます。
すると、色を黄色に変えたにもかかわらず赤のまま変わらないという結果が得られます。

f:id:halya_11:20181230222802p:plain

これはShaderファイルだけをAssetBundle化して読み込んだ場合も同様です。

解決方法

解決方法としてはShaderとcgincにちゃんとAssetBundle名を付けるというのが正しそうです。

今回のケースではMaterialとShaderとcgincにすべて同じAssetBundle名を付けても解決しますが、
実用性を考えるとShaderとcgincを一つのAssetBundleとするのがよさそうです。

f:id:halya_11:20181230224837p:plain

この方法であればcgincだけを更新して差分ビルドをしても正常に更新されます。

ちなみにですが、差分ビルドをやめてすべてのAssetBundleをビルドしなおせば
上記の対応をしなくても正常にcgincの変更が反映されるようです。
が、実用的じゃないので割愛しています。