Unity
Shader

本のようにシーン移動する

はじめに

スライドのトランジションの中に、本をめくるような形で次に進めるような奴ありますよね?
今回はunityでそれを表現するためにコルーチンとシェーダーをいじっていきます。

流れとしては

  1. めくる動作ができるようシェーダーを調節したimageを用意する。
  2. 移動前のシーンのスクリーンをReadPicxelsで取得する。
  3. imageに取得したものを入れる。
  4. imageは残したまま、シーンを移動させる。
  5. imageをめくる!

という形で進めていきます。

開発

シェーダーをいじる

こちらの記事のシェーダーコードを使わせていただいています。
http://esprog.hatenablog.com/entry/2016/03/13/130506

CreatでShaderを作り、中身を以下に差し替えます。

NextPage.shader
Shader "MBL/NextPage"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _PageTex("PageTexture", 2D) = "white" {}
        _AlphaMask("AlphaMask", Range(0, 1)) = 0.1
        _Flip("Flip",Range(-1, 1)) = 0
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float2 puv : TEXCOORD1;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _PageTex;
            float4 _PageTex_ST;
            float _AlphaMask;
            float _Flip;

            float l2(float x)
            {
                return 1 - _Flip + 0.1 * cos(x * 2);
            }

            float l1(float y)
            {
                return _Flip + 0.1 * sin(y * 3);
            }

            float l0(float x)
            {
                return x - _Flip;
            }

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.puv = TRANSFORM_TEX(v.uv, _PageTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                //コンテンツとページテクスチャ色取得
                float4 content_col = tex2D(_MainTex, i.uv);
                float4 page_col = tex2D(_PageTex, i.puv);

                //L0より右の描画を無視
                float l0_y = l0(i.uv.x);
                clip(i.uv.y - l0_y);

                //範囲内ならば暗い色に
                if (i.uv.x > l1(i.uv.y) && i.uv.y < l2(i.uv.x))
                    content_col = float4(0.5, 0.5, 0.5, 1);

                //ページ内容のうち一定の値より透明なものはページの色にすり替える
                if (content_col.a < _AlphaMask)
                    return page_col;

                return content_col * page_col;
            }
            ENDCG
        }
    }
}

新しいマテリアルを作り、その中に作ったShaderを入れます。
スクリーンショット 2018-06-08 21.49.05.png
スクリーンショット 2018-06-08 21.49.05 1.png

そして、imageのmaterialに入れます!!
スクリーンショット 2018-06-08 22.15.57.png

めくれるようになりました!!!!!
Shaderの中のFlipのスライダーをずらすとよくわかります。
これで画像を入れる土台は整いました。

imageは非アクティブにしておきしょう!。

スクリーンを取得してimageに入れる

スクリーンを取得するためにはReadPicxelsというメソッドを使っていきます。

はじめに、imageつけるスクリプトをかきます。(今回はButtonでシーン移動させます)

UIに関するスクリプトを動かすので以下を追記します。
using UnityEngine.UI;
using UnityEngine.SceneManagement;

ButtonScript.cs

public Image m_DrawTex;

public void Botton(){
        StartCoroutine ("Capture");
    }

IEnumerator Capture()
    {
        //ReadPicxelsがこの後でないと使えないので必ず書く
        yield return new WaitForEndOfFrame();

        //スクリーンの大きさのSpriteを作る
        var texture = new Texture2D(Screen.width, Screen.height);

        //スクリーンを取得する
        texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
        //適応する
        texture.Apply();

        //取得した画像をSpriteに入るように変換する
        byte[] pngdata = texture.EncodeToPNG();
        texture.LoadImage(pngdata);

        //先ほど作ったSpriteに画像をいれる
        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
        Debug.Log ("c");

        //Spriteを使用するオブジェクトに指定する
        //     今回はUIのImage
        m_DrawTex.GetComponent<Image>().sprite = sprite;

        // サイズ変更
        m_DrawTex.GetComponent<RectTransform>().sizeDelta = new Vector2(texture.width, texture.height);

        //imageをアクティブにする
        image.SetActive (true);
    }

ボタンを押したときにメソッドが呼ばれるように設定しましょう

スクリーンショット 2018-06-08 21.11.44.png

ボタンスクリプトにimageを入れておくのもお忘れなく!
スクリーンショット 2018-06-08 21.11.05.png

これで、シーン移動の直前画面をimageで写すことができるようになりました。
次にこのimageを消さないままシーン移動する方法を説明します。

imageを残したままシーン移動

imageが消えないようにするにはDontDestroyOnLoadを使います。
bool値の宣言とstart()の中に以下を追記してください。

ButtonScript.cs
public bool DontDestroyEnabled = true;

    void Start () {
        if (DontDestroyEnabled) {
            // Sceneを遷移してもオブジェクトが消えないようにする
            DontDestroyOnLoad (this);
        }
    }

更に別のメソッドを作りシーン移動ができるようにする。

Button.cs
public void nextScene(){
    //任意のシーンへ移動する
    SceneManager.LoadScene ("シーンの名前");

    //destroyできるようにする
    DontDestroyEnabled = false;
}

最後にCapture()の最後に以下を追記して移動できるようになります!

ButtonScript.cs
nextScene();

imageをめくる

あとはページをめくるだけ!!
アニメーションやスクリプトでFlipの値を動かせばページをめくるような動きでシーンが移動できます!!!

参考サイト

紙をめくるアニメーションをするシェーダー作ってみた
【Unity】画面キャプチャ
Unityで画面のスクリーンショットを撮る(Application.CaptureScreenshotじゃない方法)