一般情况下,在Unity中添加UI元素,会先添加一个主要的Canvas,游戏中的UI元素都会挂在这个Canvas下。
RenderMode选ScreenSpace Camera,再挂一个UI摄像机。参数如下:
UI Camera的Projection、Size、Clipping Planes影响着UI的渲染。
Camera的参数如下:
关于渲染顺序,这里有篇文章写得挺齐全的:Unity中影响渲染顺序的因素总结
下图是他总结的渲染图:
一般情况下,两个UI位于同一个Canvas中,它们的层级关系只需要注意UI或其父物体在Hierarchy的位置关系即可。如果不在同一个Canvas下,可以修改Canvas的SortOrder。
但是如果是特效和UI,改变Hierarchy的位置不能解决特效穿透问题。
方法一、
给UI和特效加上Canvas,修改Cannvas的SortOrder值。
因为Unity是根据Canvas绘制的,不同的Canvas尽管引用的图集相同,也会增加额外的DrawCall,所以性能消耗可能会增加。
另外也可能引起其他的层级穿透问题,不好控制层级关系。
方法二、
因为方法一踩了很多坑,在网上查找其他的解决方案的时候翻到这个文章,试了一下SpriteRenderer,该方法方法可行。
只是要注意Canvas的scale修改SpriteRenderer的scale。
方法三、
我们也可以修改shader的渲染区域来解决该问题,添加一个脚本MaskExtend,初始化一个裁剪的区域,然后修改特效的shader,使特效在裁剪区域才可以显示。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class MaskExtend : Mask
{
Camera cam;
protected override void Start()
{
base.Start();
ResetShaderMaskClip();
}
public void ResetShaderMaskClip()
{
Vector3[] points = new Vector3[4];
(transform as RectTransform).GetWorldCorners(points);
if (cam == null)
cam = GameObject.Find("Camera").GetComponent<Camera>();
float miniX,miniY,maxX,maxY;
Vector3 scPos = cam.WorldToScreenPoint(points[0]);
miniX = scPos.x;
miniY = scPos.y;
maxX = scPos.x;
maxY = scPos.y;
for (int i = 0; i < points.Length; i++)
{
scPos = cam.WorldToScreenPoint(points[i]);
//取最小xy
miniX = scPos.x < miniX ? scPos.x : miniX;
miniY = scPos.y < miniY ? scPos.y : miniY;
//取最大xy
maxX = scPos.x > maxX ? scPos.x : maxX;
maxY = scPos.y > maxY ? scPos.y : maxY;
}
Vector4 normalize = new Vector4(miniX / Screen.width, miniY / (float)Screen.height, maxX / (float)Screen.width, maxY / (float)Screen.height);
Shader.SetGlobalVector("_MaskClip", normalize);
}
}
添加mask shader
Shader "Unlit/UIMaskClip" {
Properties{
_MainTex("Texture", 2D) = "white" { }
_TintColor("Color", Color) = (0.5,0.5,0.5,0.5)
_Lighting("Lighting", float) = 1
_CutOut("CutOut", float) = 0.1
//此段可以将 ZWrite 选项暴露在Unity的Inspector中
[Enum(Off, 0, On, 1)] _zWrite("Zwrite", Float) = 0
//此段可以将 Ztest 选项暴露在Unity的Inspector中
[Enum(UnityEngine.Rendering.CompareFunction)] _zTest("ZTest", Float) = 4
//此段可以将 Cull 选项暴露在Unity的Inspector中
[Enum(UnityEngine.Rendering.CullMode)] _cull("Cull Mode", Float) = 2
//此段可以将 Blend 选项暴露在Unity的Inspector中
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Src Blend Mode", Float) = 5
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Dst Blend Mode", Float) = 10
[Enum(UnityEngine.Rendering.BlendMode)] _SrcAlphaBlend("Src Alpha Blend Mode", Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _DstAlphaBlend("Dst Alpha Blend Mode", Float) = 10
}
SubShader{
//@@@DynamicShaderTagsRepaceStart
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
//@@@DynamicShaderTagsRepaceEnd
Pass
{
//面板化
Blend[_SrcBlend][_DstBlend],[_SrcAlphaBlend][_DstAlphaBlend]
Zwrite[_zWrite]
ZTest[_zTest]
Cull[_cull]
//@@@DynamicShaderBlendRepaceStart
//@@@DynamicShaderBlendRepaceEnd
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// #pragma multi_compile CLIP_OFF CLIP_ON
sampler2D _MainTex;
float4 _MainTex_ST;
float _Lighting;
float4 _MaskClip;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
float4 projPos : TEXCOORD2;
}
;
struct appdata {
float4 vertex : POSITION;
float2 texcoord:TEXCOORD0;
float4 color : COLOR;
}
;
//顶点函数没什么特别的,和常规一样
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.color = v.color;
o.projPos = ComputeScreenPos(o.pos);
return o;
}
fixed4 _TintColor;
float4 frag(v2f i) : COLOR
{
float4 col = tex2D(_MainTex,i.uv);
col = col* _TintColor*i.color;
col.rgb *= _Lighting;
//先clip,再fog 不然会出错
int o;
o = sign(i.projPos.x - _MaskClip.x) + sign(i.projPos.y - _MaskClip.y) + sign(_MaskClip.z - i.projPos.x) + sign(_MaskClip.w - i.projPos.y);
clip(o / 4 - 0.5);
return col;
}
ENDCG
}
}
}
shader的问题,代码是别人给的。如果你有其他好方法的话,欢迎讨论。
参考:
https://blog.csdn.net/qweewqpkn/article/details/79656263
https://www.cnblogs.com/Bright-King/p/9205534.html
https://gameinstitute.qq.com/community/detail/113593
优化: