想起来测试这一个问题也是源于一次面试,问题很直接:
在UI中显示两个粒子特效,然后在它们中间放一个texture,怎么保证它们之间的层级正确
如果只需要简单地回答这个问题,那么只需要直接回答“修改Spriterenderer和粒子特效中Order In Layer参数(在Inspector中是Orider In Layer,在代码和ScreenSpace-Overlay中是sortingOrder),根据需要的层级关系进行修改即可”,但是这样只是浮在了表面,这其中的整体规律到底是怎么样的呢?
这个系列文章一共三篇加一篇总结,仅从测试使用方面来研究显示机制,有时间会再对UGUI的源码进行阅读后补充具体显示方式。
如果直接看最后的总结,可以点击这里:
在Unity的UGUI系统里使用Renderer的显示层级研究 (零) 规律总结
对Unity稍微熟悉一点的都知道,UGUI是在5.X版本才出现的东西,在那之前Unity自带的只有一套简单的GUI系统,用来做UI虽不说不是没法用,但用起来需要自己解决的问题太多,所以大家都用一个第三方的UI组件NGUI,直到现在一些相对老的项目还在使用,一些招聘需求上还写了熟悉NGUI。此外,据各种小道消息传说,UGUI是Unity官方把制作NGUI的一些人挖了过去做的一个系统。
我说这么多想表达的是什么呢?那就是UGUI和Unity三维空间里的渲染是两套系统,在UGUI框架之内的组件和普通的三维渲染对象一般情况下是没法进行很好的层级管理的(当然并不是完全没有办法,后面会说)。
关于这一点,从继承关系上也能看出来:
三维世界的一切可视对象都来自于这个组件:Renderer,一切负责显示的组件都派生自这一个类,比如MeshRenderer、SpriteRenderer、ParticleSystemRenderer等等;而UGUI中的显示对象都派生自Grapic,比如Image、Text、RawImage等等,我简单地画了一下派生关系,类中的成员我没有一一写出了,不过需要注意的是,SortingOrder这个成员变量是在Renderer中定义的,此外,在UGUI的基本组件中也有一个sortingOrder变量:
从类图上就能很简单地看出来,存在于三维空间的Render和在UGUI中进行显示的对象是两套不同的系统,如果是两个Image中想夹一个SpriteRenderer,除非这两个Image属于两个不同的canvas,否则不能实现这个功能。
对于两个三维空间的spriteRenderer物体,它们之间可以通过transform中的z轴位置进行前后摆放,Z轴大的会被Z轴小的遮挡;又可以使用sortingOrder进行控制,sortingOrding越小就会被大的遮挡;
那么sortingOrder和z值哪个优先级更高呢?
验证也很简单,我准备了四张图,分别进行编号,Z轴位置从1到4分别是1、2、3、4,在sortingOrder一样时,显示效果如下图:
这是一个非常明显的,从1到4依次被上一个遮挡;
保持这个Z轴位置不变,将sortingLayer从图1到图4依次改为1、2、3、4,显示效果如下:
与前一种正好反了过来,也就是说,sortingOrder和Z轴的优先级相比,sortingOrder的优先级更高。
首先要验证,在sortingOrder相同,并且Z轴相同时如何显示:
这里创建一个默认的粒子特效,使用图1和图2,其中图1的Z位置与粒子特效的Z位置相同,而图2的位置在Z=10的位置,显示效果如下:
很明显的,在sortingOrder相同时,距离一样会对这几个对象的显示位置造成影响,这与直接使用几个SpriteRenderer的结果是一样的。
那么再试试SortingOrder不一样的情况,保持之前的Z轴关系不变,把粒子特效的sortingOrder变为1,图2的sortingOrder变为2,结果如下:
图2的sortingLayer最大,粒子特效次之,图1最小,实际显示还是与使用几个SpriteRenderer一样,sortingLayer优先级最大。
前面试过了粒子特效和2d图片在sortingOrder的影响下的显示表现,那么3D物体也会受这个影响吗?
还是老套路,来试试就知道了。
void Start()
{
Debug.LogFormat("物品 {0} 的sortingOrder为:{1}",this.name,GetComponent().sortingOrder);
}
运行效果如下:
同时可以看到debug出来的sortingLayer,可以确认目前所有的对象都在同一个sortingOrder上。
在图上可以看到粒子特效有一部分在cube上,cube有一半被图1 遮挡,这说明这几种renderer在sortingLayer相同时都是直接受z轴影响的。
那么这时候就存在一个新的问题了,因为3d物体的遮挡在正常情况下只受z轴影响,所以在我现在这个环境中,图1会遮挡半个cube——也就是说cube上被图1遮挡的部分会显示为图1;如果图2的sortingOrder比图1要大,所有图1和图2重叠的位置都会显示为图2;又由于图2完全不会遮挡cube,那么当cube被图1遮挡的部分也被图2遮挡时,会显示图1还是图2?
还是继续试验:
保持几个位置关系不变,图1和cube的sortingOrder都保持0,但是图2改为1,显示效果如下:
可以看到,cube中间有一块显示的是图1,如果我把图1的显示关掉,会显示成下面这样:
从这个测试可以看出,在sortingOrder和z轴冲突的时候,会以3d物体的z轴为更高的优先级进行显示——换句话说,如果3d物体因为z轴位置的原因被其他物体遮挡,并且其他物体遮挡的这一部分因为sortingLayer原因被第三个对象遮挡,遮挡这一部分会无视sortingOrder而使用遮挡3d物体的部分进行渲染。
前面说了,UGUI的显示和3维空间的显示是两套不同的渲染系统,那么如果一个三维空间的物体如果放到了canvas之下,会怎么显示呢?
如果没有对Renderer和Canvas的分析,一开始就到现在这里肯定会有点抓瞎,但是基于前面那么多测试和分析,答案应该很简单:
因为是两套系统,所以它们之间的显示还是跟之前一样,因为canvas组件的渲染并不能对Renderer组件造成影响,但是因为transform的原因,还是会对物体的位置形成一定变化。
因为Canvas存在三种常用的模式,下面就这几种模式一一还原一下上面的情况