﻿Shader "UI/SliceTeleport"
{
    Properties
    {
        //以下4つの変数は適切にImageの座標を調整するために必要
        [HideInInspector]_CanvasScreenHeight("CanvasScreenHeight", float) = 1000
        [HideInInspector]_PosY("PosY", float) = 0
        [HideInInspector]_ScaleY("ScaleY", float) = 1
        [HideInInspector]_ImageHeight("ImageHeight", float) = 100

        [Space(10)]
        [HideInInspector]_SliceCount("SliceCount", Range(1, 20)) = 4 //テレポートする際の画像の分割数
        [HideInInspector]_Distance ("Distance", Range(1, 20)) = 1 //テレポートの移動距離
        [HideInInspector]_TeleportPow ("TeleportPow", Range(0, 1)) = 0 //テレポートの進捗、1で完全にテレポート完了

        [Space(10)]
        [Toggle] 
        [HideInInspector]_UseMonoCol("UseMonoCol", float) = 1 //テレポートの途中で単一色に変化するかどうか
        [HideInInspector]_MonoCol("MonoCol", Color) =  (1.0, 1.0, 1.0, 1.0) //単一色の色
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" }
        Cull Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        CGINCLUDE
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        struct appdata
        {
            fixed2 uv : TEXCOORD0;
            fixed4 vertex : POSITION;
            fixed4 color : COLOR;
        };

        struct v2f
        {
            fixed2 uv : TEXCOORD0;
            fixed4 vertex : POSITION;
            fixed4 color : COLOR;
        };

        sampler2D _MainTex;
        //アタッチされたSpriteのサイズを取得(x=1.0/width, y=1.0/height, z=width, w=height)
        float4 _MainTex_TexelSize;

        fixed _CanvasScreenHeight;
        fixed _PosY;
        fixed _ScaleY;
        fixed _ImageHeight;

        int _SliceCount;
        fixed _Distance;
        fixed _TeleportPow;

        int _UseMonoCol;
        fixed4 _MonoCol;
        ENDCG

        Pass
        {
            CGPROGRAM

            v2f vert (appdata v)
            {
                v2f o;
                v.vertex.y = v.vertex.y  * _Distance;
                o.vertex = UnityObjectToClipPos(v.vertex);
    
                //画像のHeightを変更しつつ、画像の見た目上の移動は極力抑える処理
                fixed screenHeightRatio = _ScreenParams.y / _CanvasScreenHeight;
                fixed imageRatio =  _ImageHeight / _MainTex_TexelSize.w;
                fixed texelRatio = (_MainTex_TexelSize.w * imageRatio * _ScaleY) / _ScreenParams.y;
                o.vertex.y = o.vertex.y + texelRatio * (_Distance - 1) * screenHeightRatio;
    
                //対象画像がスクリーン座標の中央から離れるほど画像の移動距離がずれるので、その修正
                o.vertex.y = o.vertex.y - _PosY * (_Distance - 1);
    
                o.uv = v.uv;
                o.color = v.color;
                return o;
            }

            //スライスした画像の中央部分の移動処理
            fixed centerSlice(fixed uvX, fixed uvY)
            {
                //_SliceCountに準じたx軸の横幅を取得
                fixed sliceLen = 1 / ((fixed)_SliceCount * 2 - 1);
                //_TeleportPowに準じた画像の移動距離を算出
                fixed TelepoNum = _TeleportPow * (_Distance - 1);

                //画像を移動させるかどうかのフラグを取得
                fixed rightEdge = step(uvX, 0.5 + sliceLen / 2);
                fixed leftEdge = 1 - step(uvX, 0.5 - sliceLen / 2);
                fixed doTelepo = rightEdge * leftEdge;

                //必要であれば画像の座標に変更を加えて返す
                return uvY + doTelepo * -TelepoNum;
            }

            //スライスした画像の中央以外の移動処理
            fixed sideSlice(fixed uvX, fixed uvY, int curSlice)
            {
                //_SliceCountに準じたx軸の横幅を取得
                fixed sliceLen = 1 / ((fixed)_SliceCount * 2 - 1);
                //画像をどのタイミングで移動させ始めるかのdelay値を取得
                fixed delay = 0.4 / ((fixed)_SliceCount - 1) * curSlice / _Distance * 4;
                //_TeleportPowに準じた画像の移動距離を算出
                fixed TelepoNum = saturate(_TeleportPow - delay) * (_Distance - 1);

                //１つ手前のスライス移動処理を実行していたかどうかを取得
                fixed prevRightEdge = step(uvX, 0.5 + sliceLen / 2 + (sliceLen * (curSlice - 1)));
                fixed prevLeftEdge = 1 - step(uvX, 0.5 - sliceLen / 2 - (sliceLen * (curSlice - 1)));
                fixed prevDidntTelepo = 1 - prevRightEdge * prevLeftEdge;

                //画像を移動させるかどうかのフラグを取得
                fixed curRightEdge = step(uvX, 0.5 + sliceLen / 2 + (sliceLen * curSlice));
                fixed curLeftEdge = 1 - step(uvX, 0.5 - sliceLen / 2 - (sliceLen * curSlice));
                fixed doTelepo = prevDidntTelepo * curRightEdge * curLeftEdge;

                //必要であれば画像の座標に変更を加えて返す
                return uvY + doTelepo * -TelepoNum;
            }

            //画像をスライスし、山なりに移動させる
            fixed2 sliceMountain(fixed2 uv)
            {
                //頂点シェーダーの時点で画像をy軸に引き伸ばしているが、見た目自体は変えない(中央に圧縮)するための処理
                fixed y = uv.y * _Distance;
                //_TeleportPowの値に応じて少しだけ見た目のy軸を引き伸ばす
                fixed baseTelepoSt = 1 + _TeleportPow * (_Distance * 1) * 0.5;
                y = y / baseTelepoSt;

                //中央部分を切り出し画像をズラす
                y = centerSlice(uv.x, y);
                fixed uvX = uv.x;

                //中央部分以外を切り出し画像をズラす
                for(int i = 1; i < (fixed)_SliceCount; i++)
                {
                    y = sideSlice(uvX, y, i);
                }

                fixed2 fixUv = fixed2(uvX, y);
                return fixUv;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //ピクセルを_TeleportPowに準じて上に移動
                fixed2 originalUv = i.uv;
                fixed2 sliceUv = sliceMountain(i.uv);
                fixed4 col = tex2D(_MainTex, sliceUv);

                //_TeleportPowが上昇するにつれて画像を単一色に変更
                fixed fixPow = saturate(_TeleportPow * 4);
                fixed3 monoCol = fixPow * _MonoCol + (1 - fixPow) * col;
                col.rgb = _UseMonoCol * monoCol + (1 - _UseMonoCol) * col.rgb;

                col *= i.color;
                return col;
            }
            ENDCG
        }
    }
}