最近在做一个富文本插件YLYRichText,里面用到了ui动画表情,效果如下:
其原理是在shader里面按照一定的速度改变uv,按顺序计算每一帧的uv偏移量和宽高比例,抓取序列帧图片里面每一帧的区域渲染出来。
代码里面有详细说明哈:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
Shader "Yly/YlyUISeqFrameAni"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
_RowCount ("RowCount", float) = 0
_ColCount ("ColCount", float) = 0
_Speed ("Speed", float) = 30
//用在ui上的shader一般都需要加下面的模板测试逻辑,避免被有mask组件的父节点遮罩时没遮罩效果
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_ColorMask("Color Mask", Float) = 15
}
SubShader
{
Tags
{
"Queue" = "Transparent" //一般ui都用这种渲染队列,从远到近渲染
"IgnoreProjector" = "True" //忽略投影,一般ui的shader为提高效率都会设置为true
"RenderType" = "Transparent"
"PreviewType" = "Plane" //材质球预览模式为面片
"CanUseSpriteAtlas" = "True" //设置_MainTex可以使用Sprite(2D and UI)类型的贴图
}
//用在ui上的shader一般都需要加下面的模板测试逻辑,避免被有mask组件的父节点遮罩时没遮罩效果
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Cull Off
Lighting Off //关掉光照,一般ui的shader为提高效率都会这样设置
//下面两句设置ZTest只根据ui节点的树层次(即在Hierarchy视图中的层次)作为依据进行测试,不根据z值
ZWrite Off
ZTest[unity_GUIZTestMode]
//
Blend SrcAlpha OneMinusSrcAlpha //最终颜色值 = 输出颜色值 * 输出颜色Alpha值a + 背景颜色值 * (1 - a),即如果有透明物品挡在前面的话,类似隔着透明玻璃看东西那种效果
ColorMask[_ColorMask]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Speed;
float _RowCount;
float _ColCount;
v2f vert (appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//从左往右,从上往下进行序列帧播放
float totalCount = _ColCount * _RowCount; //总帧数,例如 _RowCount = 2,_ColCount = 5,totalCount = 5 * 2 = 10
float curIndex = floor((_Time.y * _Speed) % totalCount); //当前第几帧,例如 curIndex = 8
float2 unitSize = float2(1 / _ColCount, 1 / _RowCount); //每一帧所占大小比例,例如 unitSize = float2(1/5, 1/2)
float offsetU = floor(curIndex % _ColCount); //uv起点u方向偏移量,例如 offsetU = floor(8 % 5) = 3
float offsetV = floor((totalCount - 1 - curIndex) / _ColCount); //uv起点v方向偏移量,例如 offsetV = floor((10 - 1 - 8)/5) = 0
float2 originUv = float2(offsetU, offsetV) * unitSize; //uv起点偏移量比例,例如 originUv = float2(3 * 1/5, 0 * 1/2) = float2(3/5, 0)
float2 newUv = originUv + i.uv * unitSize; //newUv = uv起点偏移量比例 + uv大小比例
//例如 ui四个顶点新的uv坐标计算如下:
//左下角newUv = float2(3/5, 0) + uv(0, 0) * float2(1/5, 1/2) = float2(3/5, 0) + float2(0, 0) = float2(3/5, 0)
//左上角newUv = float2(3/5, 0) + uv(0, 1) * float2(1/5, 1/2) = float2(3/5, 0) + float2(0, 1/2) = float2(3/5, 1/2)
//右上角newUv = float2(3/5, 0) + uv(1, 1) * float2(1/5, 1/2) = float2(3/5, 0) + float2(1/5, 1/2) = float2(4/5, 1/2)
//右下角newUv = float2(3/5, 0) + uv(1, 0) * float2(1/5, 1/2) = float2(3/5, 0) + float2(1/5, 0) = float2(4/5, 0)
fixed4 col = tex2D(_MainTex, newUv);
return col;
}
ENDCG
}
}
}
|
材质球只需要设置这三个参数就行啦。
把代码跑起来一边看效果一边看代码理解更快哦^_^。