Unity Sheet Animation Shader

# 概要
Unity上でSheet Animationを使ったシェーダーの例です。
Sheet Animationを使うことで板ポリ1枚とテクスチャ1枚でアニメーションを表現することができます。
連番アニメーションはAnimatorを用いることで実装することもできますが、Shaderを使った方が使用するコンポーネントも少なく軽量です。
また、アニメーションの際にMaterialを変化させないので、MaterialのInstance化を防ぎバッチを効かせることができます。

![fire.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105021/09e9241e-5206-be11-6e00-f805c50cb777.gif)

[Unity Sheet Animation Shader Graph版](https://qiita.com/broken55/items/1800c4f348378d609f4c)

# ソースコード
```glsl
Shader "Unlit/SheetAnimation"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
        _MainColor("Main Color",Color) = (1,1,1,1)
        _Row("Row",int) = 4
        _Column("Column",int) = 4
        _Speed("Speed",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _MainColor;

            uint _Row;
            uint _Column;
            float _Speed;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //全てのコマ数を計算
                uint cells = _Row * _Column;
                //時間にSpeedを掛けて小数点以下だけを返すことで0～1を生成
                fixed process = frac(_Time.y * _Speed);
                //index=どのコマを指しているか
                uint index = process * cells;

                //1コマ当たりのUVサイズを計算
                float invRow = 1.0 / (float)_Row;
                float invColumn = 1.0 / (float)_Column;

                float2 tiling = float2(invRow,invColumn);
                
                //indexからどのコマに当たるかを計算する
                half x = (index % _Row) * invRow;
                //画像の左上からコマが始まるのでひっくり返している
                half y = 1 - (((index / _Row) +1) * invColumn);
                
                float2 offset = float2(x,y);

                //テクスチャのオフセットを適用する
                fixed4 col = tex2D(_MainTex, (i.uv * tiling) + offset);
                //色を変えられるようにする
                col *= _MainColor;
                
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

```

# 解説
- Unlit Shaderから作成を開始しています
    - 今はOpaqueになってますが、`Blend Mode`を変えることで半透明シェーダーとして使うこともできます
- `_Time.y`の時間で変化する変数を用いて、テクスチャの表示する位置を変化させています
- メインのテクスチャでTilingやOffsetを設定できないよう`[NoScaleOffset]`の属性を付与しています
    - Sheet Animationの場合はTexture全体を使うのが一般的なので、TilingやOffsetを設定する必要は無いはず

- Sheet Animationのコマ割りの順番は、下の画像のように左上から右下に進むことを想定しています

![texturetest001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105021/98f09457-bc7a-feac-23b1-933a41b55189.jpeg)

- もし順番が違う場合はoffsetを計算する部分のコードを書き換える必要が出てきます
    - 例えば左下からスタートする場合は下記のようになります

```glsl
half y = ((index / _Row) +1) * invColumn;
```