Unity Shaders and Effects Cookbook:修改UV坐标实现帧动画

发表于2017-11-24
评论0 1.9k浏览

在2D游戏中,如果要做动画效果,一般是让美术制作连续帧的动画,然后我们用代码控制图片连续播放,这样就实现了帧动画,在shader中也可以实现这个效果。


上篇文章大家已经学会了如何改变UV坐标来实现水流效果,这篇文章我们就通过改变UV坐标来实现帧动画效果。


美术给我们的帧动画的图可能是一张一张的小图,也可以是类似于下面的大图图集。


在 Shader 中实现帧动画的原理:

默认的,Shader 中的 UV 范围是 (0,1) ,也就是说默认的是整个图集 这张大图显示出来,如下图


这里有9张小图 如果要显示 第一张小图,该怎么办?

只要把 UV的范围修改成 ( 0 , 1/9 ) 就是了,那么从 (0,1) 到 ( 0 , 1/9 ) ,只要乘以 1/9  就可以了。

然后根据Unity提供的内置变量 _Time.y 进行取整,每次 _Time.y 增加 1 就移动 1/9,然后加上位移,那么

从 0 - 1s 这个时间段,取整为 1 显示的都是 第一张图 ( 0 , 1/9 ) 的x 范围,即第一张小图。

从 1 - 2s 这个时间段, 取整为 2 显示的都是 第一张图 ( 1/9 , 2/9 ) 的x范围,即第二张小图。


Shader完整代码

Shader "CookBookShaders/Sprite Sheet Anim" {  
    Properties {  
        _MainTex ("Base (RGB)", 2D) = "white" {}  
    }  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
        LOD 200  
        CGPROGRAM  
        #pragma surface surf Lambert  
        sampler2D _MainTex;  
        struct Input {  
            float2 uv_MainTex;  
        };  
        void surf (Input IN, inout SurfaceOutput o)   
        {  
            fixed2 spriteUV = IN.uv_MainTex;  
            float cellPercentage = 1.0 / 9; //每一个小图占百分比;  
            float xValue=spriteUV.x;  
            //UV默认是(0,1),就是说默认显示整个大图,我们要显示一个小图,UV要指定到(0,1/9)的范围;  
            xValue=xValue*(1.0/9);  
            //再对时间取整,如果 _Time.y 时间超过了1,就加一个小图这么宽的位移,也就是把x 指到下一个小图的范围。就显示出下一个小图  
            xValue+=cellPercentage * ceil(_Time.y);  //_Time.y 等同于 Time.timeSinceLevelLoad,就是游戏运行时间  
            //再赋值;  
            spriteUV = float2(xValue,spriteUV.y);  
            half4 c = tex2D (_MainTex, spriteUV);  
            o.Albedo = c.rgb;  
            o.Alpha = c.a;  
        }  
        ENDCG  
    }   
    FallBack "Diffuse"  
}  

这里再次提醒 _Time 是一个 float4变量,值为 值为 Time (t/20, t, t*2, t*3), t 等同于 Time.timeSinceLevelLoad,就是游戏开始运行时间。

所以我这里取的 _Time.y 就是游戏开始运行到现在的时间,ceil ( _Time.y ) 就是游戏运行到现在有多少秒,每秒变动一次图片。



书上也提到,在计算 当前应该将 UV 偏移到第几张小图 这里,可以把这个计算移动到 CPU中,用以节省 GPU 。

那新建一个脚本,内容如下:

using UnityEngine;  
using System.Collections;  
public class SpriteSheetAnim : MonoBehaviour {  
    float cellIndex;  
    // Use this for initialization  
    void Start () {  
    }  
    // Update is called once per frame  
    void Update ()   
    {  
    }  
    void FixedUpdate()  
    {  
        cellIndex = Mathf.Ceil(Time.time);  
        transform.renderer.material.SetFloat("_cellIndex", cellIndex);  
    }  
} 

在 FixedUpdate 中计算,仍然是对 当前时间进行了取整,来计算出当前应该偏移到第几张图。

把脚本挂在 Quad 上。运行后效果同上面的图。


另外书上的 Shader 代码写的太复杂……

其中用到了另一个内置函数 fmod:

fmod ( x, y) 返回 x / y 的余数,即 x % y ,符号同 x ,如果 y =0 那么结果不可预料。 


ceil ( x ) 对 x 向上取整,如 ceil ( 0.6 ) == 1 。 


示例工程打包下载:http://pan.baidu.com/s/1skqBKmx

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引