【个人UNITY笔记】{基础} 2D游戏中使用Shader或Camera解决Sprite前后遮挡关系

因为个人制作的奇葩游戏2D游戏是用Sprite当作角色和物体,然而Unity里不应该使用带透视的相机来渲染Sprite,因为会2个Sprite距离太近会出现闪烁,但是我就是想保留透视,同时使用Sprite,于是想了几个方法。

在Unity中制作2D游戏,前后遮挡关系总是让我很头疼。在PS里面有图层的概念,上面一层永远在下面一层的上面。然而Unity对于Sprite的渲染顺序很奇怪,是相机当前位置,到Sprite中心点的直线距离。距离越近,就越后渲染。看似没什么问题,但是因为是相机的当前位置,所以当2个Sprite相隔太近就会出现闪烁的现象,就像2个Cube交叉放在一起会出现闪烁一样,因为2个Sprite到相机的距离太接近了,导致Unity不知道谁前谁后。

【个人UNITY笔记】{基础} 2D游戏中使用Shader或Camera解决Sprite前后遮挡关系_第1张图片
例如我自己做的一个奇葩项目,斜45度的一个2D游戏,角色应该在宝石前面,现在看起来是正常的

【个人UNITY笔记】{基础} 2D游戏中使用Shader或Camera解决Sprite前后遮挡关系_第2张图片
侧面看是这样的

【个人UNITY笔记】{基础} 2D游戏中使用Shader或Camera解决Sprite前后遮挡关系_第3张图片
然而摄相机转到一定角度就会这样

有人会问,2D游戏相机不会转到这里啊,确实,但是因为我想保留一些透视的感觉,所以相机并没有调成正交视角,2个Sprite距离太近,到屏幕边缘的时候还是会错位。


这里,屏幕边缘的地方还是会错位

于是我就想能否手动指定Unity的渲染顺序,这里有2种方法。

1.多Camera分层渲染法
在Camera组件里面有Culling Mask一栏属性,这里是选择此相机渲染的Layers层。

【个人UNITY笔记】{基础} 2D游戏中使用Shader或Camera解决Sprite前后遮挡关系_第4张图片

此外还要在Clear Flags属性中选择Depth Only,即只渲染Culling Mask里选择的Layers层。
这样这个相机就只会显示出这一Layers层的物体,然后我们注意到Camera还有一个Depth的属性,用这个属性就可以控制多个摄影机渲染的先后次序了,Depth数字越小,渲染的次序就越前,即显示在后面。

这里我用了5个相机渲染5个Layers,**注意,depth最小的相机Clear Flags得是Skybox或者Solid Color,不然没有物体的地方会鬼畜**

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

你可能感兴趣的:(unity,游戏,2d,基础)