因为个人制作的奇葩游戏2D游戏是用Sprite当作角色和物体,然而Unity里不应该使用带透视的相机来渲染Sprite,因为会2个Sprite距离太近会出现闪烁,但是我就是想保留透视,同时使用Sprite,于是想了几个方法。
在Unity中制作2D游戏,前后遮挡关系总是让我很头疼。在PS里面有图层的概念,上面一层永远在下面一层的上面。然而Unity对于Sprite的渲染顺序很奇怪,是相机当前位置,到Sprite中心点的直线距离。距离越近,就越后渲染。看似没什么问题,但是因为是相机的当前位置,所以当2个Sprite相隔太近就会出现闪烁的现象,就像2个Cube交叉放在一起会出现闪烁一样,因为2个Sprite到相机的距离太接近了,导致Unity不知道谁前谁后。
例如我自己做的一个奇葩项目,斜45度的一个2D游戏,角色应该在宝石前面,现在看起来是正常的
有人会问,2D游戏相机不会转到这里啊,确实,但是因为我想保留一些透视的感觉,所以相机并没有调成正交视角,2个Sprite距离太近,到屏幕边缘的时候还是会错位。
这里,屏幕边缘的地方还是会错位
于是我就想能否手动指定Unity的渲染顺序,这里有2种方法。
1.多Camera分层渲染法
在Camera组件里面有Culling Mask一栏属性,这里是选择此相机渲染的Layers层。
此外还要在Clear Flags属性中选择Depth Only,即只渲染Culling Mask里选择的Layers层。
这样这个相机就只会显示出这一Layers层的物体,然后我们注意到Camera还有一个Depth的属性,用这个属性就可以控制多个摄影机渲染的先后次序了,Depth数字越小,渲染的次序就越前,即显示在后面。
Depth数字大的相机所渲染的物体永远在Depth数字小的相机渲染的物体上面。(仅游戏画面中,Scene窗口中还是会错位)
弊端:a.Layers的数量是有限的,加上Unity本身的8个内置的Layers,一共只有32个Layers层。因此不能分太多层,而且占用了Layers,对其他同样需要使用Layers做Mask的组件不太友好。如果项目本身就很小,而且美术资源分配合理,用不了太多层的话,这个方法挺好。
b.效率的问题,小项目无所谓辣。
2.Shader分层渲染法
Unity为了方便我们在Shader的属性中内置了一个Queue标签来让我们控制渲染队列,渲染队列是Shader渲染的顺序,效果和PS的图层很相似。
看看Unity默认的渲染队列把
渲染队列 | 渲染队列描述 | 渲染队列值 |
---|---|---|
Background | 这个队列被最先渲染。它被用于skyboxes等。 | 1000 |
Geometry | 这是默认的渲染队列。它被用于绝大多数对象。不透明几何体使用该队列。 | 2000 |
AlphaTest | 通道检查的几何体使用该队列。它和Geometry队列不同,对于在所有立体物体绘制后渲染的通道检查的对象,它更有效。 | 2450 |
Transparent | 该渲染队列在Geometry和AlphaTest队列后被渲染。任何通道混合的(也就是说,那些不写入深度缓存的Shaders)对象使用该队列,例如玻璃和粒子效果。 | 3000 |
Overlay | 该渲染队列是为覆盖物效果服务的。任何最后被渲染的对象使用该队列,例如镜头光晕。 | 4000 |
看到这里可能再看看实际的Shader内容吧,这里我选择了官方内置的Sprite/Diffuse来修改,因为我想让Sprite接受光源所以选择了Diffuse没有选择Default。
Unity内置Shader可以在Unity官方网站上下载到。
下面是修改的Shader
Shader "Depth/2900/2900"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent-100" //这里的Transparent在上面的表里是3000,减100就是2900,代表这个Shader的渲染队列在2900,低于2900渲染队列的Shader都显示在此Shader下面,反之则显示在上面。
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
CGPROGRAM
#pragma surface surf Lambert vertex:vert nofog keepalpha
#pragma multi_compile _ PIXELSNAP_ON
#pragma shader_feature ETC1_EXTERNAL_ALPHA
sampler2D _MainTex;
fixed4 _Color;
sampler2D _AlphaTex;
struct Input
{
float2 uv_MainTex;
fixed4 color;
};
void vert (inout appdata_full v, out Input o)
{
#if defined(PIXELSNAP_ON)
v.vertex = UnityPixelSnap (v.vertex);
#endif
UNITY_INITIALIZE_OUTPUT(Input, o);
o.color = v.color * _Color;
}
fixed4 SampleSpriteTexture (float2 uv)
{
fixed4 color = tex2D (_MainTex, uv);
#if ETC1_EXTERNAL_ALPHA
color.a = tex2D (_AlphaTex, uv).r;
#endif //ETC1_EXTERNAL_ALPHA
return color;
}
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = SampleSpriteTexture (IN.uv_MainTex) * IN.color;
o.Albedo = c.rgb * c.a;
o.Alpha = c.a;
}
ENDCG
}
Fallback "Transparent/VertexLit"
}
如果是Transparent-99则渲染队列是2901,以此类推。
这样只要把想显示在上面的物体附上2901,下面的物体附上2900,则2901的物体会永远在2900上面,即使在Scene视图中也是如此
如图渲染通道数字高的永远在上面显示
弊端:理论上可以解决所有遮挡问题,如果项目很小,没必要使用这一种,用第一种即可。
参考网址:http://blog.sina.com.cn/s/blog_89d90b7c0102v9xj.html