尊重原创,转载请在文首注明出处:http://blog.csdn.net/cai612781/article/details/78477944
项目中经常遇到层级的显示问题,来总结下。
一,Unity
按优先级从高到低排列:
1,Camera.depth
depth小的先渲染。
2,Renderer.sortingLayerID
sortingLayerID为基类Renderer的属性,sortingLayerID小的先渲染。
3,Renderer.sortingOrder
sortingOrder同为基类Renderer的属性,sortingOrder小的先渲染。
sortingLayerID和sortingOrder虽然是基类Renderer的属性,但只适用于Unity2d的排序,因此我们在面板中可以看到只有显示图片的SpriteRenderer和显示粒子的
ParticleSystemRenderer有这两个属性。
4,Material.renderQueue
该属性对应的是Shader的renderQueue,renderQueue的范围必须是[0,5000],数值小的先渲染。Shader中使用SubShader的Queue标签来设定模型属于哪个渲染队列。
Unity内置了几个渲染队列的枚举:
public enum RenderQueue
{
Background = 1000,//最先渲染,通常用来绘制背景
Geometry = 2000,//不透明物体的渲染
AlphaTest = 2450, // 需要透明度测试的物体,在不透明物体渲染后再渲染会更高效
GeometryLast = 2500, // 分界线,[0,2500]为不透明物体,[2501,5000]为半透明物体
Transparent = 3000,//半透明物体的渲染,任何使用了透明度混合(例如关闭了深度写入)的物体都用得用该队列
Overlay = 4000,//用于叠加效果
}
上述排序是在Shader关闭了深度写入(ZWrite Off)的情况下,当开启了深度写入时,物体的空间位置(transform.position.z)会影响层级,z值小的在前面。
另外,Unity菜单Edit->Project Settings->Tags中的Layers,只是一个逻辑的分层,用于Camera的Culling Mask来设置相机渲染哪个Layer的物体,辅助Camera.depth来管理层级。
二,NGUI
按优先级从高到低排列:1,UIPanel.sortingOrder
通过源码可以看到UIPanel.sortingOrder更改的就是上文中Renderer.sortingOrder。
2,UIPanel.depth
depth小的先渲染
3,UIWidget.depth
depth小的先渲染,NGUI根据UIWidget.depth的排序合并drawcall(因此尽量将相同材质的图集在depth上排在一起可以减少UI上的drawcall)。
UIPanel面板还有个Render Q属性,用来设置该面板的renderQueue的起始值,所有面板总的起始值默认为3000。该属性有三种选项,分别是
Automatic,StartAt,Explicit,后两者可自定义renderQueue的起始值。
当Render Q默认为Automatic时,根据UIPanel.depth的排序对drawcall的材质的renderqueue进行排序。
当Render Q默认为StartAt或Explicit时,UIPanel.depth则不起作用,根据自定义的值对drawcall的材质的renderqueue进行排序。
NGUI所用的shader都是关闭了深度写入的,因此z值不影响层级。所以NGUI本质还是通过Unity的sortingOrder和renderQueue来管理层级。
三,NGUI与模型或特效的夹层问题
我们在处理模型或特效显示在两个UI之间的层级显示问题,有两种方式:
1,利用Camera.depth属性,将界面A,模型或特效,界面B分别放在三个Layer,用三个depth递增的相机来渲染。(该方式不推荐,因为一个界面上可能会打开新的界面,以及两个界面的相对层次不是固定的,这样会导致相机不断增加,层次管理混乱)
2,利用renderQueue属性,给两个UIPanel之间留下一定范围的renderQueue数值,将模型或特效的renderQueue设置在这个范围内。(推荐该方式)
代码如下:
第一步,新增renderQueue控制脚本:RenderQueueContro.cs
using System.Collections.Generic;
using UnityEngine;
public class RenderQueueControl : MonoBehaviour
{
private UIPanel _panel;
private int _rq;
private List<Material> _materialList = new List<Material>();
private int i, len;
void Start()
{
}
public void Init()
{
_panel = NGUITools.FindInParents<UIPanel>(gameObject);
UIPanel.AddModelRQ(this);
InitMaterials();
}
private void InitMaterials()
{
_materialList.Clear();
Renderer[] ren = GetComponentsInChildren<Renderer>();
if(ren != null)
{
for(i = 0, len = ren.Length; i < len; i++)
{
_materialList.AddRange(ren[i].materials);
}
}
}
public void UpdateRQ()
{
if(_panel != null && _panel.drawCalls.Count > 0)
{
_rq = _panel.drawCalls[_panel.drawCalls.Count - 1].renderQueue + 1;
for(i = 0, len = _materialList; i < len; i++)
{
if(_materialList[i].renderQueue != _rq)
{
_materialList[i].renderQueue = _rq;
}
}
}
}
void OnDestroy()
{
_materialList.Clear();
UIPanel.RemoveRQ(this);
}
}
第二步,给两个UIPanel之间留下一定范围的renderQueue数值,并在UIPanel更新时修改模型的renderQueue。我们修改UIPanel.LateUpdate(注释为修改处)。
void LateUpdate()
{
if(mUpdateFrame != Time.frameCount)
{
mUpdateFrame = Time.frameCount;
for(int i = 0, imax = list.Count; i < imax; ++i)
{
list[i].UpdateSelf();
}
int rq = 3000;
for(int i = 0, imax = list.Count; i < imax; ++i)
{
UIPanel p = list[i];
if(p.renderQueue == RenderQueue.Automatic)
{
p.startingRenderQueue = rq;
p.UpdateDrawCalls();
rq += p.drawCalls.Count * 2;//加上*2
}
else if(p.renderQueue == RenderQueue.StartAt)
{
p.UpdateDrawCalls();
if(p.drawCalls.Count != 0)
{
rq = Mathf.Max(rq, p.startingRenderQueue + p.drawCalls.Count * 2);//加上*2
}
}
else
{
p.UpdateDrawCalls();
if(p.drawCalls.Count != 0)
{
rq = Mathf.Max(rq, p.startingRenderQueue + 1 * 2);//加上*2
}
}
}
UpdateModelRQ();//加上刷新rq
}
}
private static List<RenderQueueControl> _modelRQList = new List<RenderQueueControl>();
public static void AddModelRQ(RenderQueueControl control)
{
if(!_modelRQList.Contains(control))
{
_modelRQList.Add(control);
}
}
public static void RemoveModelRQ(RenderQueueControl control)
{
_modelRQList.Remove(control);
}
private void UpdateModelRQ()
{
for(int i = 0, len = _modelRQList.Count; i < len; i++)
{
if(_modelRQList[i] == null)
{
continue;
}
_modelRQList[i].UpdateRQ();
}
}
第三步,加载模型或特效后的调用
public static void AddModelParent(GameObject modelGo, Transform parentTran)
{
if(modelGo == null || parentTran == null || parentTran.gameObject == null)
{
return;
}
modelGo.transform.parent = parentTran;
int layerUi = parentTran.gameObject.layer;
SetLayerRecursively(parentTran.gameObject, layerUi);
RenderQueueControl rqControl = modelGo.AddMissingComponent<RenderQueueControl>();
rqControl.Init();
}
public static void SetLayerRecursively(GameObject parent, int layer)
{
if(parent == null)
{
return;
}
parent.layer = layer;
foreach(Transform child in parent.transform)
{
if(child == null)
{
continue;
}
SetLayerRecursively(child.gameObject, layer);
}
}
效果图