Unity中的内置时间变量
名称 | 类型 | 描述 |
_Time | float4 | t是自该场景加载开始所经过的时间,4个分量的值分别是(t/20, t, 2t 3t)。 |
_SinTime | float4 | t是时间的正弦值,4个分量的值分别是(t/8, t/4, t/2, t) |
_CosTime | float4 | t是时间的余弦值,4个分量的值分别是(t/8, t4, t/2, t) |
unity_DeltaTime | float4 | dt是时间增量,4个分量的值分别是(dt, 1/dt, smoothDt, 1/smoothDt) |
序列帧动画
最常见的纹理动画之一就是序列帧动画。序列帧动画的原理非常简单,它像放电影一-样,依次播放一系列关键帧图像,当播放速度达到一定数值时,看起来就是一个连续的动画。它的优点在于灵活性很强,我们不需要进行任何物理计算就可以得到非常细腻的动画效果。而它的缺点也很明显,由于序列帧中每张关键帧图像都不一样,因此,要制作一张出色的序列帧纹理所需要的美术工程量也比较大。
Shader "Unity Shaders Book/Chapter 11/Image Sequence Animation" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Image Sequence", 2D) = "white" {} //包含了所有关键帧图像的纹理
_HorizontalAmount ("Horizontal Amount", Float) = 4 //水平方向关键帧图像个数
_VerticalAmount ("Vertical Amount", Float) = 4 //竖直方向关键帧图像个数
_Speed ("Speed", Range(1, 100)) = 30 //播放速度
}
SubShader {
//半透明标签
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _HorizontalAmount;
float _VerticalAmount;
float _Speed;
struct a2v {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
float time = floor(_Time.y * _Speed); //模拟的时间
float row = floor(time / _HorizontalAmount); //行索引
float column = time - row * _HorizontalAmount; //列索引
half2 uv = i.uv + half2(column, -row);
uv.x /= _HorizontalAmount;
uv.y /= _VerticalAmount;
fixed4 c = tex2D(_MainTex, uv);
c.rgb *= _Color;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
由于序列帧图像通常包含了透明通道,因此可以被当成是一个半透明对象。在这里我们使用半透明的“标配”来设置它的SubShader标签,即把Queue和RenderType设置成Transparent,把IgnoreProjector设置为True。在Pass中,我们使用Blend 命令来开启并设置混合模式,同时关闭了深度写入。
要播放帧动画,从本质来说,我们需要计算出每个时刻需要播放的关键帧在纹理中的位置。而由于序列帧纹理都是按行按列排列的,因此这个位置可以认为是该关键帧所在的行列索引数。因此,在上面的代码的前3行中我们计算了行列数,其中使用了Unity的内置时间变量_Time。 _Time.y 就是自该场景加载后所经过的时间。我们首先把_Time.y 和速度属性_Speed 相乘来得到模拟的时间,并使用CG的floor函数对结果值取整来得到整数时间time。然后,我们使用time除以HorizontalAmount 的结果值的商来作为当前对应的行索引,除法结果的余数则是列索引。接下来,我们需要使用行列索引值来构建真正的采样坐标。由于序列帧图像包含了许多关键帧图像,这意味着采样坐标需要映射到每个关键帧图像的坐标范围内。我们可以首先把原纹理坐标i.uv按行数和列数进行等分,得到每个子图像的纹理坐标范围。然后,我们需要使用当前的行列数对上面的结果进行偏移,得到当前子图像的纹理坐标。需要注意的是,对竖直方向的坐标偏移需要使用减法,这是因为在Unity中纹理坐标竖直方向的顺序(从下到上逐渐增大)和序列帧纹理中的顺序(播放顺序是从上到下)是相反的。这样,我们就得到了真正的纹理采样坐标。
如何用Unity实现序列帧动画:
为序列帧素材更改Texture Type为Sprite模式,Sprite Mode 更改为Multiple(否则无法更改切片), Filter Mode 更改为 Point ,Compression更改为 None(无压缩),点击Apply。
![]()
在PackageManager中添加2D Sprite,点击Sprite Editor, 设置slice(根据素材大小设置每一块切分的大小)。
但此方法应用前提为导入的是一张GIF素材,切割后点击运行可以形成序列帧动画。