Unity翻书效果

unity 翻页效果 unity翻书效果_Tex


目前做的VR项目中需要一个翻阅魔法书的效果,考虑过使用UnityBookPageCurl-master插件,但是那个插件是纯UI显示的,只有二维效果,在VR里观感不佳,之后在网上找到一个写好的翻页shader,于是结合找到的shader写了一套多页翻书的代码。

shader如下:

http://www.45fan.com/article.php?aid=19113086500012186887046431

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Personal/PageTurning" {
    Properties
    {
     _Color("Color", Color) = (1,1,1,1)
     _MainTex("MainTex",2D) = "White"{}
     _SecTex("SecTex",2D) = "White"{}
     _Angle("Angle",Range(0,180)) = 0
     _Warp("Warp",Range(0,10)) = 0
     _WarpPos("WarpPos",Range(0,1)) = 0
     _Downward("Downward",Range(0,1)) = 0
    }
        SubShader
     {
      pass
      {
       Cull Back

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

       struct v2f
       {
        float4 pos : POSITION;
        float2 uv : TEXCOORD0;
       };
       fixed4 _Color;
       float _Angle;
       float _Warp;
       float _Downward;
       float _WarpPos;
       sampler2D _MainTex;
       float4 _MainTex_ST;

       v2f vert(appdata_base v)
       {
        v2f o;
        v.vertex += float4(5,0,0,0);
        float s;
        float c;
        sincos(radians(-_Angle),s,c);
        float4x4 rotate = {
        c,s,0,0,
        -s,c,0,0,
        0,0,1,0,
        0,0,0,1};
        float rangeF = saturate(1 - abs(90 - _Angle) / 90);
        v.vertex.y += -_Warp * sin(v.vertex.x * 0.4 - _WarpPos * v.vertex.x) * rangeF;
        v.vertex.x -= rangeF * v.vertex.x * _Downward;
        v.vertex = mul(rotate,v.vertex);

        v.vertex += float4(-5,0,0,0);
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
        return o;
       }

       fixed4 frag(v2f i) :COLOR
       {
        fixed4 color = tex2D(_MainTex,-i.uv);
        return _Color * color;
       }


       ENDCG
      }

      pass
      {
       Cull Front

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

       struct v2f
       {
        float4 pos : POSITION;
        float2 uv : TEXCOORD0;
       };
       fixed4 _Color;
       float _Angle;
       float _Warp;
       float _Downward;
       float _WarpPos;
       sampler2D _SecTex;
       float4 _MainTex_ST;

       v2f vert(appdata_base v)
       {
        v2f o;
        v.vertex += float4(5,0,0,0);
        float s;
        float c;
        sincos(radians(-_Angle),s,c);
        float4x4 rotate = {
        c,s,0,0,
        -s,c,0,0,
        0,0,1,0,
        0,0,0,1};
        float rangeF = saturate(1 - abs(90 - _Angle) / 90);
        v.vertex.y += -_Warp * sin(v.vertex.x * 0.4 - _WarpPos * v.vertex.x) * rangeF;
        v.vertex.x -= rangeF * v.vertex.x * _Downward;
        v.vertex = mul(rotate,v.vertex);

        v.vertex += float4(-5,0,0,0);
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
        return o;
       }

       fixed4 frag(v2f i) :COLOR
       {
        float2 uv = i.uv;
        uv.x = -uv.x;
        fixed4 color = tex2D(_SecTex,-uv);
        return _Color * color;
       }
       ENDCG
      }
     }
}

注意,翻页的图片需要将“贴图间拼接模式”从“钳制”改为“重复”。

unity 翻页效果 unity翻书效果_unity_02


之后是功能逻辑块代码编写,目前的思路是书的左页和右页是静止不动的,翻页shader主要满足翻书动效,在翻书动画结束后则将翻书物体的shader关闭,之后替换书的左页图片和右页图片,层级目录如下:

unity 翻页效果 unity翻书效果_unity 翻页效果_03


功能代码如下:

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 打开书本及翻书功能
/// 1、打开书本和关闭书本
/// 书本由合着的状态变为打开的状态,打开后显示第一页
/// 2、翻页功能:
/// 从第一页翻到第二页
/// 在书本打开后,显示第一页,需要翻到第二页时,先将翻页物体的第一张和第二张贴图设置成第一页的后半张及第二页的前半张
/// 在翻页成功后,将书本的左页设置成第二页的前半张,书本的右页设置成第二页的后半张,之后将翻页物体隐藏
/// </summary>
public class Book_WSY : MonoBehaviour
{

    /// <summary>
    /// 首页
    /// </summary>
     GameObject FirstPage;

    /// <summary>
    /// 尾页
    /// </summary>
     GameObject LastPage;

    /// <summary>
    /// 翻页
    /// </summary>
     GameObject OverTurnPage;

    [Header("全部书页")]
    public List<Sprite> sprites = new List<Sprite>();

    [Header("翻页时间")]
    public float time = 1;

    /// <summary>
    /// 当前页数
    /// </summary>
    int initPageNum = 0;
    // Start is called before the first frame update
    void Start()
    {
        FirstPage = transform.Find("首页").gameObject;
        LastPage = transform.Find("尾页").gameObject;
        OverTurnPage = transform.Find("翻页").gameObject;

        FirstPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum].texture);
        LastPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum + 1].texture);

        //关闭翻页物体
        OverTurnPage.gameObject.SetActive(false);
    }

    /// <summary>
    /// 打开书本
    /// </summary>
    void OpenBooKEvent()
    {

    }

    /// <summary>
    /// 关闭书本
    /// </summary>
    void CloseBookEvent()
    {

    }

    /// <summary>
    /// 翻转书本--正翻
    /// 从第一页翻到第二页
    /// 在书本打开后,显示第一页,需要翻到第二页时,先将翻页物体的第一张和第二张贴图设置成第一页的后半张及第二页的前半张
    /// 在翻页成功后,将书本的左页设置成第二页的前半张,书本的右页设置成第二页的后半张,之后将翻页物体隐藏
    /// </summary>
    void OverTurnBookEvent()
    {
        if(initPageNum+2>= sprites.Count||timer>Time.time)
        {
            return;
        }
        timer = Time.time + JianGe;
        //打开翻页物体
        OverTurnPage.gameObject.SetActive(true);
        //设置翻页物体的贴图
        OverTurnPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum+1].texture);
        OverTurnPage.GetComponent<MeshRenderer>().material.SetTexture("_SecTex", sprites[initPageNum+2].texture);

        //先将尾页设置成当前页数+2
        LastPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum + 3].texture);
        //开始翻页
        int rotateVal = 0;
        
        DOTween.To(() => rotateVal, x => rotateVal = x, 180, 1).OnUpdate(delegate
        {
            OverTurnPage.GetComponent<MeshRenderer>().material.SetInt("_Angle", rotateVal);
        }).OnComplete(delegate
        {
            //翻页结束
            //将首页替换为当前页数+1
            FirstPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum + 2].texture);

            //关闭翻页物体
            OverTurnPage.gameObject.SetActive(false);
            initPageNum+=2;
        });

    }

    /// <summary>
    /// 逆翻书效果
    /// </summary>
    void OverTurnBookBackEvent()
    {
        if (initPageNum - 2 < 0||timer>Time.time)
        {
            return;
        }
        timer = Time.time + JianGe;

        //打开翻页物体
        OverTurnPage.gameObject.SetActive(true);
        //设置翻页物体的贴图
        OverTurnPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum - 1].texture);
        OverTurnPage.GetComponent<MeshRenderer>().material.SetTexture("_SecTex", sprites[initPageNum].texture);

        //先将首页设置成当前页数-2
        FirstPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum - 2].texture);
        //开始翻页
        int rotateVal = 180;

        DOTween.To(() => rotateVal, x => rotateVal = x, 0, 1).OnUpdate(delegate
        {
            OverTurnPage.GetComponent<MeshRenderer>().material.SetInt("_Angle", rotateVal);
        }).OnComplete(delegate
        {
            //翻页结束
            //将未页替换为当前页数-1
            LastPage.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", sprites[initPageNum - 1].texture);

            //关闭翻页物体
            OverTurnPage.gameObject.SetActive(false);
            initPageNum -= 2;
        });
    }


    float timer;
    float JianGe = 1.1f;

    // Update is called once per frame
    void Update()
    {

        if (Input.GetKeyDown(KeyCode.A))
        {
            OverTurnBookEvent();
        }

        if (Input.GetKeyDown(KeyCode.S))
        {
            OverTurnBookBackEvent();
        }
        

        
    }
}

示例项目工程地址如下:
https://gitee.com/wang-senyu/book-page-turning-effect/tree/master/