文/罗培羽
一、图片切割
游戏开发中,常常把多张图片素材合并成图集,然后程序读取所需的部分显示。如下展示的是一张人物行走图,程序需要把它切分成12张小图加以显示。一种做法是使用Sprite Editor切分图集,当做多张小图来处理。如果Image能够只显示图集的一部分,程序还是把图集当做1张图片处理,可以减少不小工作量。本文提供一种使用shader实现上述功能的例子。
1、编写Shader
下面的着色器代码使用了顶点/片元着色器处理图片切割功能。这里定义5个属性,其中_MainTex代表图片贴图,_ColCount和_RowCount代表图片的列数和行数,_ColIndex和_RowIndex表示要显示哪一行哪一列的图片。
核心代码是“o.uv.x = (_ColIndex + v.texcoord.x)/_ColCount”和“o.uv.y = (_RowIndex + v.texcoord.y)/_RowCount”,它们实现了UV坐标的变换。“o.uv.x = (_ColIndex + v.texcoord.x)/_ColCount”即是“o.uv.x = _ColIndex*(1/_ColCount) + v.texcoord.x *(1/_ColCount)”的化简式,由于纹理坐标被归一化到[0,1]的范围,1/_ColCount即表示每一张小图的宽度。
Shader "Lpy/ImageClip"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_ColCount ("Column Count", int) = 4
_RowCount ("Row Count", int) = 4
_ColIndex ("Col Index", int) = 0
_RowIndex ("Row Index", int) = 0
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass
{
Tags { "LightMode"="ForwardBase" }
ZTest off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
int _ColCount;
int _RowCount;
int _ColIndex;
int _RowIndex;
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 = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.x = (_ColIndex + v.texcoord.x)/_ColCount;
o.uv.y = (_RowIndex + v.texcoord.y)/_RowCount;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
2、使用材质
新建一个名为ImageClip的材质,选择上述编写的shader。将ColumnCount和RowCount设置为图集的列数和行数,如下图所示。
将刚创建的材质应用于图片上,如下图所示。
在Scene或Game视图中观察图片,改变材质的ColIndex和RowIndex属性,即可显示不同的小图。如下图所示。
3、代码控制
如下代码展示使用脚本控制材质属性的方法,当按下空格键时,改变材质的RowIndex属性,展现不同小图。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class RoleCtrl : MonoBehaviour
{
public Image image;
private Material mtl;
// Use this for initialization
void Start ()
{
mtl = image.material;
}
// Update is called once per frame
void Update ()
{
if (Input.GetKeyDown (KeyCode.Space))
{
int row = mtl.GetInt("_RowIndex");
row++;
if(row >= 4)
row = 0;
mtl.SetInt("_RowIndex", row);
}
}
}
二、图片翻转
2D游戏开发中,往往会使用翻转的图片(如下图),然而Transform的旋转并不能完成这一功能,那么什么办法将图片翻转呢?
1、编写Shader
下面的着色器代码使用了顶点/片元着色器处理图片翻转功能。这里定义3个属性,其中_MainTex代表图片贴图,_Hor代表是否启用水平翻转,_Ver代表是否启用垂直翻转。核心代码是“o.uv.x = (1-v.texcoord.x)*_Hor + v.texcoord.x*(1-_Hor)”和“o.uv.y = (1-v.texcoord.y)*_Ver + v.texcoord.y*(1-_Ver);”它们实现了UV坐标的变换。在o.uv.x的计算式中,如果_Hor为0,那么“o.uv.x = v.texcoord.x”即为原始UV,如果_Hor为1,由于纹理坐标被归一化到[0,1]的范围,那么“o.uv.x = 1-v.texcoord.x”即为翻转后的UV。
Shader "Lpy/ImageFlip"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Hor ("Is Horizontal Filp", Range (0, 1)) = 0
_Ver ("Is Vertical Filp", Range (0, 1)) = 0
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass
{
Tags { "LightMode"="ForwardBase" }
ZTest off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
int _Hor;
int _Ver;
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 = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.x = (1-v.texcoord.x)*_Hor + v.texcoord.x*(1-_Hor);
o.uv.y = (1-v.texcoord.y)*_Ver + v.texcoord.y*(1-_Ver);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
2、使用材质
新建一个名为ImageFilp的材质,选择上述编写的shader,设置Is Horizontal Filp和Is Vertical Filp,如下图所示。
将刚创建的材质应用于图片上,即可看到翻转的效果。如下图所示。
最后依然到了广告时间:笔者即将出版的一本Unity3D实战类书籍《Unity3D网络游戏实战》。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路,感谢大家支持。
知乎专栏:https://zhuanlan.zhihu.com/p/24739023
|