一、介绍两大UI插件NGUI和UGUI
二、unity渲染顺序控制方式
三、NGUI的控制
四、UGUI的控制
五、模型深度的控制
六、粒子特效深度控制
七、NGUI与模型和粒子特效穿插层级管理
八、UGUI与模型和粒子特效穿插层级管理程序员
这篇笔记是整理了以前作的记录,在作项目的过程当中,遇到了各类各样的界面穿插问题,界面层级混乱,好比,手机卡了或点快了,就致使两个界面相互交叉。对于界面,这应该算是一个很严重的bug,很大部分缘由是整个UI框架没有从总体上考虑这个,后来决心弄清楚层级的控制,并把一些对于目前项目可行的方法应用,界面穿插的问题少了不少,注意我只是在现有的框架打的补丁。若是是一个从头开始,在架构UI的时候,这篇笔记应该会颇有用。架构
之前项目使用的NGUI插件,UI是有一我的摆放的,咱们客户端就拿到这些prefab,添加相应的逻辑脚本。但偏偏是这个摆界面的人也没有注意界面的层级,各个panel、各个weight的depth没有统一管理,致使开发中后期,界面一多起来,不少界面同时出现的时候,panel和weight的层级就乱了。框架
unity自带的GUI系统
通常都不用它来开发游戏,做为拓展unity编辑器开发比较多,unity自带的GUI效率很是低,每次渲染都是一个DrawCall,很差用,不能作到所见即所得。编辑器
unity4.6以后的UGUI
UGUI是NGUI做者参与开发的,unity官方的新UI系统,感受挺好用的,至于效率问题应该比NGUI好些。5.x之后ugui有很大的提高,之后ugui应该会逐步替代ngui。
很早以前就尝试过这套ugui系统,正是我在研究界面层级的时候,用NGUI显示模型粒子特效没有找到好的方法时,因而探索了ugui,具体怎么控制层级,后面会讨论。函数
NGUI
是一个老牌的unity UI插件了,在ugui没有推出以前,大部分unity的游戏都使用的NGUI插件开发UI,它的好处不少,提供了常见的UI控件,实现几乎全部须要的功能,在效率上也是控制严谨,DrawCall合并极大提高了控件渲染效率,还支持3D GUI。可是也有不少不足,版本更新太频繁,版本bug不少,对于了解NGUI的开发者能够很好利用它,但对于初学者来讲,NGUI当然容易上手好用,可是对DrawCall的重建规则不了解,仍然会效率低下(后面会整理一下分析NGUI的笔记)。布局
FairyGUI
最近发现的一款跨平台UI编辑器,组合各类复杂UI组件,以及为UI设计动画效果,无需编写代码,能够一键导出Unity,Starling,Egret, LayaAir,Flash等多个主流应用和游戏平台。性能
特性:
所见即所得。操做简易,使用习惯与Adobe系列软件保持一致,美术设计师能够轻松上手。
在编辑器便可组合各类复杂UI组件,无需编写代码。不须要程序员编码扩展UI组件。
强大的文本控件。支持动态字体,位图字体,以及BMFont制做的位图字体,支持HTML语法和UBB语法,支持图文混排。
强大的列表控件,支持多种布局,支持虚拟列表和循环列表,即便列表项目数量巨大也拒绝卡顿。
支持图片的九宫格和平铺处理,支持图片变色和灰度,支持序列帧动画编辑和使用。
内置手势支持。
提供时间轴设计UI动效,可实时看到每帧的位置或其余效果。
编辑状态下使用分散的素材,发布时自动打包图集。支持定义多个图集,自动支持抽出A通道的压缩方式。
多国语言支持。
各类分辨率自适应。
提供插件机制,能够根据项目的须要为编辑器加入个性功能。
为各个游戏平台提供了一致的API,得益于编辑器强大的编辑功能,程序员只须要了解少许API就能完成UI展示,相比Feathers, NGUI, UGUI等UI框架,FairyGUI提升了UI制做效率,下降了成本。测试
固然不止这些,还有许多UI框架,好比:Daikon Forge GUI、EZ GUI、2dTookit等等,有时间能够看看,但目前主要用到的仍是NGUI、UGUI啦。字体
若是是2D游戏,渲染顺序关系着每一个层次的显示前后,好比UI在游戏内容前面,游戏内容又有多层次。举一个简单的例子,在横版2D游戏中,常常会用到多层滚动的背景,把游戏物体分层管理起来,能够有效的减小出错概率,很好的控制显示效果。动画
对于3D游戏,游戏内容是3D的,UI通常是2D,有不少时候须要把某个模型啊,粒子特效啊,放在界面上,这样就有一个问题,3D物体和2D界面的前后关系,好比有些界面是在模型之上的,有些在下面,尝试过不少种办法,都能实现需求,但不是每种办法都是那么舒服的。
Camera
Camera是unity中最优先的渲染顺序控制。depth越大,渲染顺序越靠后。
sortingLayer 和 sortingOrder
对于这个属性,我也是云里雾里的,不是太明白。按照字面意思是层的排序,Canvas和Renderer都有这个属性,unity官方就一句话带过。优先级仅次于Camera的depth。
看网上的博客,通常都写着sorting layer是比Camera低一层级的控制渲染顺序的属性。而后我作了不少尝试,发现并非全部Renderer组件这个属性做用,根据尝试的结果,作以下总结:
Canvas中的sorting layer能够控制Canvas的层级,这是ugui中的东西,下面会讨论的;
GameObject中的renderer属性就是挂在该GameObject的Renderer组件,Renderer是渲染组件的基类,下面有多个派生类,通过测试,目前知道的只有SpriteRenderer的sorting layer和sorting order能控制渲染顺序。因此我也以为,sorting layer和 sorting order就是unity 对2D游戏所支持的吧;
renderer组件有以下几类:
RenderQueue
这是unity中的一个概念,(至少以前在opengl中没听过),大体意思就是渲染顺序,那无疑就是控制渲染顺序的嘛。
因此通常设置材质的renderQueue或直接在shader中设置。
在ShaderLab中,有4个提早定义好的render queue,你能够设置更多的在他们之间的值:
Background :表示渲染在任何物体以前
Geometry(default):渲染大多数几何物体所用的render queue
AlphaTest:用于alpha测试
Transparent:用于渲染半透明物体
Overlay:渲染全部物体之上
There are four pre-defined render queues, but there can be more queues in between the predefined ones. The predefined queues are:
Background - this render queue is rendered before any others. You’d typically use this for things that really need to be in the background.
Geometry (default) - this is used for most objects. Opaque geometry uses this queue.
AlphaTest - alpha tested geometry uses this queue. It’s a separate queue from Geometry one since it’s more efficient to render alpha-tested objects after all solid ones are drawn.
Transparent - this render queue is rendered after Geometry and AlphaTest, in back-to-front order. Anything alpha-blended (i.e. shaders that don’t write to depth buffer) should go here (glass, particle effects).
Overlay - this render queue is meant for overlay effects. Anything rendered last should go here (e.g. lens flares).
好了,讲完了unity中的控制渲染顺序的方法后,接下来聊两个个UI系统独有的方法了。首先说明一下,NGUI中空间位置不会影响屏幕显示关系,由于在它内部处理顶点的时候,舍弃了Z坐标值。
UIPanel
首先讲到的就是UIPanel,用过NGUI插件的朋友应该很是熟悉这个组件,未来会整理一篇分析NGUI的笔记,里面会重点聊到UIPanel,因此这篇笔记就一笔带过了:UIPanel很是重要。哈哈。
depth:
NGUI中最正统的控制panel之间层级关系的就是 它的 depth 属性。depth越大,越靠后渲染。
sorting order:
panel的该属性也能够控制panel的顺序。
它的优先级在depth以前,内部仍是经过设置render的sorting order来控制的。来看看一些源码:
UIPanel.cs
public int sortingOrder { get { return mSortingOrder; } set { if (mSortingOrder != value) { mSortingOrder = value; #if UNITY_EDITOR NGUITools.SetDirty(this); #endif UpdateDrawCalls(); } } }
而后查找了mSortingOrder的索引,发如今函数UpdateDrawCalls中使用了mSortingOrder
继续跟踪dc.sortingOrder
而后发现mRenderer是个 MeshRenderer类型的,在以前还说过,对render的sorting order不太明白,并不能控制渲染顺序。然而NGUI内部却使用了而且可以控制渲染顺序。我就纳闷了,而后在网上查了好久的资料,终于在雨松博客的评论区看到:unity中3D物体的Z的渲染区域 和UI的区域不同,必须一样都是一个片才行。NGUI中生成的UIDrawCall里面的Mesh都是片,因此sorting order对它有用,而以前说的之间建立一个cube,它是一个3D物体,3D物体和UI没办法经过它来控制层级的。
是由于panel的depth控制着UIDrawCall的生成顺序,影响了RenderQueue的顺序,前面已经提到过了,sorting order和sorting layer比RenderQueue优先级更高。
UIWeight的depth
在同一个panel下的各个weight,能够用weight的depth属性来控制它们的先后关系。depth控制着weight在panel顶点重建时传入的顶点序列,这个跟踪一下NGUI源码就知道了。(后面我会整理几篇分析NGUI的文章,里面包含这个)
RenderQueue
在NGUI中使用这个属性,就得先看看UIPanel关于RenderQueue的三种方式:
Automatic:由NGUI自动生成
StartAt:手动设置一个值
Explicit:同一个panel下的RenderQueue的值相同
看代码能够理解一下,UIPanel.cs
Canvas
不一样Canvas之间能够用如下两个属性控制渲染层级
Sorting Layer
Order in Layer
Canvas和NGUI的UIPanel同样,这些计算都不会管两个之间的父子关系,有一个算一个。
Hierarchy中顺序
在同一个Canvas中,Hierarchy的顺序决定了控件的层级关系。
空间深度
对于3D物体的显示前后就是彻底按空间的前后来的,固然能够在fragment shader中关闭深度测试,或进行其余影响帧缓冲区的操做。就跟opengl中同样了。
RenderQueue
RenderQueue是对unity中全部能够渲染的物体都适用。
补充:用Sorting Order能够控制片模型的层级关系,NGUI中sorting order就是就是靠这种特性实现的。
空间深度
和3D模型一致。
RenderQueue
和3D模型一致。
sorting order
粒子系统自己是一个Renderer组件,它渲染的是一个一个精灵,是一个一个片,该属性有效。
通过上面的整理,已经明白了各个地方是能够怎么控制渲染顺序了,接下来就来解决一些项目中遇到的问题。
NGUI中放3D模型,实现方法:
1. 多个相机
这种方法确定能够实现,并且简单粗暴。但仔细想一想,要作的决不只仅加个相机这么简单,我以前作的项目就是这么搞得。而后……而后……在开发的中后期,各类bug就出来了,因为多相机没有管理好,各类问题真的很难缠,呵呵底层不是我写的哈,因此咱们上层开发人员就补丁啊,整个项目代码有些地方面目全非。
多相机其实就是利用Camera的depth来控制渲染顺序的,通常来说,模型一个相机,UI一个相机,等等……你觉得两个相机就够了吗?有没有想过模型上可能还有界面呢?你可能说再加一个相机,可是有些需求每一个界面的跳转都是多个的,并不能直接在作界面的时候就肯定哪一个界面在上面哪一个界面在下面,因此这样的加多个相机并不可行。
后来想到一种办法,就是每一个界面对应一个相机,一个相机照一个模型,或是在一块儿显示的模型,利用相机的depth来控制它们的遮挡关系,可是须要在客户端框架中管理好相机,作一个“池子”,让相机能够复用,而且。可能你会担忧性能问题,但这不是问题,一个游戏中可能有不少界面,可是须要同时显示的最多不超过五、6个吧,加上模型须要的,每次同时工做的撑死了就10个了,并且只要让相机只照射到相应的界面,这就不会形成性能的损失,不一样的只是变换矩阵而已。
用RenderQueue控制
相比于相机的管理,我以为用RenderQueue来控制会更简单一些,毕竟NGUI中也大量使用了RenderQueue来控制先后关系,能够看看它们源码:
这是在UIPanel中的LateUpdate,能够看到三种模式下,RenderQueue具体值加的方式,通常咱们都是用Automatic模式,这种模式下是根据每一个UIPanel中生成的DrawCall自动计算RenderQueue的值。
无论3D模型、粒子特效、仍是NGUI,均可以用RenderQueue来控制渲染顺序,因此我想到了一个办法。
修改NGUI的源码,让两个UIPanel的RenderQueue值间隔一些,空出几个值用来设置给模型或粒子特效,很简单:
看吧,是否是很简单。而后只须要设置显示在界面上的模型或粒子特效的RenderQueue为这些空出来的值就好了。
这个脚本挂在相应的模型上,target表示 该模型要显示在哪一个panel下面。
A、B两个界面,我把渲染顺序调整后:A — 模型 — B
到这里只作好了一部分,咱们只是利用RenderQueue控制了渲染顺序,可是空间的深度关系仍是在影响着在屏幕上显示的前后关系。在fragment shader中会进行深度测试,无论你谁先渲染谁后渲染,只要全部物体渲染时都写入深度缓冲区,那么渲染顺序并不能真正影响最后屏幕显示的前后关系。
因此还须要一步,就是在渲染模型时关闭深度测试,例如A、B两个界面,模型在AB之间,那么渲染流程以下:
A — 关闭深度测试 — 模型 — 开启深度测试 — B
在shader lab中关闭深度测试的方法是:
ZWrite Off
这里提供一个简单的shader:
Shader "Custom/DepthTest" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "Queue"="Transparent" } ZWrite Off LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
补充:模型与模型之间也能够用上面的这种渲染流程控制屏幕显示的前后关系。
每次只显示一个界面
在同一时间,屏幕上只显示一个界面,每次打开新界面就隐藏下面的界面,连模型和粒子都隐藏掉。这样就不存在两个界面同时显示的状况了。
空间深度
在其它控制条件都相同的状况下,NGUI和模型在一个相机下,受正常的渲染管线控制,也就是空间位置关系能够影响渲染到屏幕的层级关系。注意模型的“厚度”。
粒子特效也可使用Camera的depth来控制,可是粒子特效会存在不少,因此用Camera并不可行,因此这里就直接排除掉了。
Sorting Order
粒子特效自己是用“点精灵”渲染的,每一个粒子就是一个点精灵,能够看作一个片模型,而片模型就能够受该属性影响。
能够作一个测试,把下面脚本挂在粒子特效上:
public class SortUtil : MonoBehaviour { void Awake() { renderer.sortingOrder = 1; } }
sorting Order默认值为0,现有A、B两个界面,把A的panel设置为0, B的panel设置2:
A:0
粒子特效:1
B:2
粒子特效恰好插在A、B之间,显示效果也是粒子特效穿插在A、B之间。
补充:unity5.3后粒子特效支持了直接设置sorting order
RenderQueue
这里就没有模型和NGUI交叉麻烦了,只须要完成RenderQueue的设置就能够了,不须要改shader。
假设A、B两个界面,RenderQueue的值的大小关系:
A < 粒子特效 < B
参照上面模型的具体方法,让粒子特效的RenderQueue插在A、B两个界面之间就能够了。
多个相机
一样的,能够用多个相机来作,但这种方式总归是不太好,不推荐。
shader lab
在fragment shader中利用传入的Mask数据,去模型片断数据进行筛选,符合条件的留下,不符合的丢弃。这种办法其实也适用于NGUI。
具体怎么作的能够参考雨松momo的一篇博文:这里。
RenderTexture
UGUI中Image能够接受一个材质,能够把RenderTexture放在一个材质上。这样就能够按照UGUI自己的那些排序方式来控制了。
sorting order
和NGUI同样,一样能够用sorting order来控制,Canvas组件支持这些属性。
雨松momo也过一篇文章,我这里就再也不说了,这里
写了这么多,大体总结了常见的方式,比较熟悉NGUI,因此对于NGUI中各类都比较清楚,写得比较详细。对UGUI可能总结的不是很完整,因此之后还会继续总结。在文章中提到了几处shader lab的地方,我在这里说明一下,利用shader lab也能够完成以上各类遮挡关系,层级关系,但这篇文章没有详细讲清楚,主要考虑到篇幅,因此后来会专门写shader lab的实现方法,其思想和雨松momo的差很少。
以上这些东西都是在项目中实际遇到的问题,以及思考并试着用过的,如今将笔记整理成这篇文章,有什么错误直接留言一块儿讨论,一块儿成长。