ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unity UI渲染排序全解析:从Canvas到Sorting Order的层级控制

Unity UI渲染排序全解析:从Canvas到Sorting Order的层级控制 1. UI渲染排序的底层逻辑搞清楚“谁在谁上面”做Unity项目这几年我最常被问到的渲染问题就是“为什么我的UI被3D物体挡住了”“为什么这个按钮点不到”“粒子特效到底怎么才能显示在UI上面”。这些问题十有八九都能归到同一个源头UI渲染排序。排序没梳理清楚就会出现各种看起来像是“引擎bug”的灵异现象其实底层规则非常固定。先讲一个最重要的背景。Unity每一帧绘制画面的顺序大致是清屏、绘制不透明物体、绘制半透明物体、最后绘制UI。UI的默认Shader基本都走透明渲染队列但在Screen Space Overlay模式下Canvas并不是某个相机里的三维物体而是引擎在最后阶段用一个独立的“覆盖层”逻辑提交到屏幕。这就是UI默认能盖住绝大多数3D物体的根本原因。只要你还在用Overlay模式就别幻想一个普通3D粒子能天然显示在UI前面除非你专门做处理。我把UGUI实际参与排序的维度整理成下面的优先级关系排查问题时从头到尾对照一遍基本都能定位Camera的Depth也就是相机深度决定多个相机的渲染先后。Canvas组件的Sorting Layer在不同Canvas之间排序时最先被比较。Canvas组件的Sorting Order同Sorting Layer下数值更大者后绘制显示在上层。渲染模式相关的深度信息Screen Space Camera看Plane DistanceWorld Space看世界坐标Z。Hierarchy层级顺序同一个Canvas内部同级子物体越靠下越后绘制。这里有个高频误区很多人以为给UI元素设置Sorting Layer或者Order in Layer就能调整它和另一个UI元素的上下关系结果改了半天没用。原因很简单单个Image、Text本身并没有Sorting Layer和Order in Layer这两个字段它们属于Canvas的一部分只有Canvas组件才有这些排序参数。同一个Canvas内部元素的绘制顺序完全由Hierarchy的层级顺序决定Canvas之间的排序规则管不到Canvas内部的具体元素。这句话值得记下来能省掉你大量Debug时间。2. Canvas三种渲染模式排序方式完全不同Canvas的Render Mode有三种Screen Space Overlay、Screen Space Camera、World Space。很多项目从头到尾只用默认的Overlay遇到特殊需求就开始硬调其实换一种模式反而更干净。2.1 Screen Space Overlay最省事但也最“粗暴”Overlay模式不需要指定相机UI直接覆盖在屏幕最上层。这种模式的优势是接入成本极低性能也相对好因为它不参与相机矩阵计算。劣势在于它没有“深度”概念很难把某个3D对象插到UI层之间。在Overlay模式下多个Canvas之间的排序逻辑是先比Sorting Layer再比Sorting Order如果两者都相同就比Hierarchy顺序后创建或者层级靠后的Canvas会绘制在上层。所以同一个场景里所有Overlay Canvas最好养成设置Sorting Order的习惯不要都默认写0否则哪天多加了几个弹窗Canvas谁上谁下就只能看Inspector里的顺序了。另外Overlay模式下Canvas的缩放和屏幕分辨率强相关UI在不同分辨率下会出现各种适配问题但那是另一个话题。这里只需要记住Overlay就是“永远在最上面”别再为3D物体和UI的穿插问题去调它的渲染队列方向不对。2.2 Screen Space Camera用Plane Distance控制前后Screen Space Camera模式下UI会被放置到指定相机前方的一个平面上整块UI像一个“画板”悬浮在三维空间中。这个模式最大的好处是支持真正的深度关系UI可以进入3D场景也可以被其他物体遮挡。这个模式下的排序规则稍微复杂一点。首先还是比Sorting Layer和Sorting Order如果Canvas之间的这两个值一致则根据Canvas到相机的距离排序离相机更近的Canvas后绘制所以会显示在上层。这个距离由Canvas组件上的Plane Distance控制数值越小离相机越近。我自己的项目中主界面常用的配置是UICamera用正交模式挂在主相机之外专门负责渲染UI。这样UI既能拥有深度信息又不会和场景里的3D物体混在一起穿帮。如果你要让某个UI弹窗和主UI插在中间层级只需要单独建一个Canvas调高Sort Order再把Plane Distance设置在合适的位置即可。2.3 World Space把UI当3D物体摆放World Space模式最符合直觉UI就是一个3D物体可以旋转、缩放也可以和场景里其他物体发生真实的前后遮挡。数字孪生、AR、VR项目里大量使用这种模式比如设备标签、场景中的浮动面板、角色头顶血条。World Space Canvas的排序本质上是Unity通用渲染器之间的排序。先看Sorting Layer和Order in Layer如果两个Canvas相同则通过物体距离相机的远近来决定谁先谁后离相机更近的绘制在后遮挡住远处的UI。这个规则和普通3D物体的近大远小、近处遮挡远处的直觉是一致的。所以World Space下如果出现UI被墙壁挡住、被其他模型吃掉之类的现象先不要急着改Shader优先去检查Canvas的Sorting Layer、Order in Layer还有物体本身和相机之间的距离。很多时候只是Order in Layer没拉开或者Sorting Layer没有规划好层级。3. 同一个Canvas内部如何精确控制单个UI的上下跨Canvas的排序规则很清晰真正让新人头疼的是同一个Canvas内部多个UI元素之间的遮挡控制。比如一个战斗飘字突然被头像UI遮住或者弹窗上的关闭按钮点不到。这时候需要的是Canvas内部的排序知识。3.1 Hierarchy顺序就是大部分情况的答案UGUI在同一个Canvas内部通过Hierarchy深度和同级顺序决定每个Graphic的绘制先后。这个顺序可以理解为深度优先遍历Unity先递归进入第一个子对象把它和它的所有子UI元素依次提交然后再处理下一个兄弟节点。后提交的元素会绘制在前面也就是说Hierarchy里靠下的同级UI显示在靠上的同级UI之上。所以想让某个Image显示在另一个Image前面最直接的办法就是把前者的父节点顺序往下调。运行时用代码操作常见的是transform.SetAsLastSibling()和transform.SetAsFirstSibling()一个置底一个置顶。很多新手去买各种UI排序插件其实大部分需求用这两个API就能解决。值得注意Canvas内部的这个顺序和CanvasRenderer的渲染批次强相关。调整层级顺序会改变绘制顺序同时也可能破坏Unity现有的批处理组合带来额外的DrawCall。后面讲性能时会专门展开。3.2 用“同一级下置顶”实现弹窗、飘字、提示框弹窗、飘字、新手引导这类需要“压在其他UI上面”的元素我建议不要直接把它们塞进主UI的Hierarchy里靠调顺序实现置顶。那样会让主UI的层级结构彻底变成一团乱麻每加一个弹窗都要小心翼翼。我更推荐两种做法。简单场景下在弹窗物体根节点上单独挂一个Canvas组件同时关掉Graphic Raycaster之外的其他组件把这个Canvas的Sorting Order设成远高于主界面的值。这样弹窗虽然逻辑上仍然属于同一个父物体但渲染时Unity会把它当作一个独立Canvas来处理自动排在主UI上方Hierarchy顺序就不会再影响它。另一种做法是准备一个名叫UIRootTop的节点专门用于挂载飘字、提示条、新手引导箭头。这个节点本身挂CanvasSorting Order设定为项目里最大值。所有置顶元素全部放在这个节点下逻辑和渲染都清晰。3.3 跨Canvas排序的实战配置举例我在项目里通常把整个UI体系划分成几层每一层都用独立的Canvas承载层名Sorting LayerSort Order主要用途WorldBackUI03D场景内嵌UI如地面标记UIBackUI100背景装饰、远古UI底图UIMainUI200主界面、功能面板UIPopupUI300弹窗、对话框UITopUI400飘字、提示、新手引导、特效这套分层的核心思路是Sorting Layer统一叫UISort Order从100到400拉开间隔给未来扩展留出余地。每个Canvas内部再有复杂遮挡时用SetAsLastSibling和子Canvas处理。只要各层之间的Sort Order不重叠弹窗永远在功能面板上方飘字永远在弹窗上方不需要每次做新界面都重新理一遍层级。4. 特效与UI混合的正确姿势UI和特效的混合排序是项目里最容易被问爆的问题。核心矛盾在于默认Overlay模式下UI永远在最上层普通3D粒子根本不可能显示在UI前面但美术又经常要求“按钮特效盖住按钮”“角色脚下光环穿透UI”。4.1 场景一3D粒子特效想在UI上方显示想在UI上面显示粒子有几个常见方案我按踩坑程度从低到高推荐。如果你的UI还没大量铺开建议直接换成Screen Space Camera模式给UI单独挂一个相机粒子系统放在UI相机和主相机之间的一个专用特效相机里通过相机Depth和Canvas的Plane Distance互相配合。这套方案能保留真实的粒子深度效果最自然但要改动整个UI框架适合在项目早期规划。如果项目已经用了Overlay模式不想大改最省力的做法是用RawImage加Render Texture。把粒子特效用另一个相机单独渲染到一张RenderTexture上再当成普通UI图片显示在需要的位置。优点是思路简单UI层级完全可控缺点是每个特效都要多一个相机和纹理内存和DrawCall成本偏高动态特效多了以后会明显拖慢帧率。还有一种偏门的做法是给粒子Shader改写深度写入和渲染队列让粒子强行插到UI队列之间。这个方案我不推荐因为它需要Shader层面的精细控制而且不同平台表现可能不一致。我见过团队为了把一个命中特效显示在UI上方去改Shader最后在部分Android机型上出现花屏排查了很久。能用结构解决的问题尽量不要靠Shader去硬钻。4.2 场景二UI上播放特效但需要限制在某个子区域很多时候我们要求特效只在某个按钮范围内播放超出部分不显示比如按钮高亮光晕、卡牌升级闪耀。这个需求看起来是渲染排序本质上是裁剪。解决方案最常用的是Mask组件也就是在特效UI父节点上挂一个Image加Mask把子区域之外的粒子裁剪掉。但这里有个坑Mask会打断UI批处理而且如果粒子数量很大裁剪本身也会增加额外开销。UI界面卡顿问题有相当一部分就是这种不限次数叠加Mask造成的。另一个做法是使用RectMask2D它比Mask轻量因为它没有额外的模板缓冲操作更适合裁剪UI元素。但是粒子系统能不能被RectMask2D正确裁剪需要实际测试不同粒子Shader的表现有差异。我的经验是优先把粒子渲染到RenderTexture里再对这个RawImage做RectMask2D裁剪控制力最强也不会受到粒子系统内部状态干扰。4.3 场景三角色头顶血条在VR/Pico4项目里的排序处理做Pico等VR设备的Unity项目时UI排序比手机更敏感。VR里如果继续用Overlay UI很容易出现眩晕感因为UI没有真实深度眼睛聚焦和大脑预期不一致。所以VR项目的角色血条、面板、提示文字一律建议用World Space Canvas挂在角色上方固定位置。World Space血条会被角色模型遮挡这个问题很常见我的处理方法是给血条单独设一个Sorting Layer叫UIWorldOrder in Layer设为明显高于普通3D物体的值然后关闭血条Canvas的遮挡交互。这里要注意World Space Canvas的Sorting Layer如果和3D的Sorting Layer相同同样取Order in Layer比较如果Sorting Layer不同则提前把自定义Layer顺序设好确保UIWorld在Default之上。另外VR项目里血条最好不要直接放在角色骨骼正上方因为人物转头或抽搐时血条会跟着晃动。实践中可以把血条挂在角色根节点上只做位置偏移不要挂到具体骨骼节点。这点虽然不属于渲染排序但和“血条显示层级”耦合很深我也一并写在这里。5. 排序做不对很容易拖累性能排序从来不只是“谁看见谁”的问题排序结构直接决定Canvas如何重建、如何批处理进而决定UI跑起来卡不卡。5.1 Overdraw与透明区域重绘UI界面上大量全屏半透明黑色遮罩、复杂圆角图片叠在一起时同一个像素可能被画了四五遍这就是Overdraw。Overdraw在移动端尤其致命因为GPU的填充率是有限度的。UI卡顿如果发生在打开某个功能面板时第一步就去检查这个面板是否引入了大面积的透明叠加层。可以用Unity编辑器右上角的Overdraw模式查看屏幕重绘情况颜色越亮代表重绘越多。正常的UI主界面应该保持中低Overdraw弹窗可以稍微高一点但大面积亮白区域意味着UI层级叠了太多透明物料。从排序层面能做的优化是能并排的不要重叠能隐藏的不要半透明透明区域尽量不做。5.2 Canvas重建为什么频繁改排序会让UI卡顿UGUI一个特点是Canvas内容发生变化时需要重建网格。重建的本质是重新执行一次布局计算和顶点计算然后把结果提交给GPU。如果每一帧都有人调用SetAsLastSibling、修改UI的alpha、或者频繁显示隐藏弹窗这些操作都会触发Canvas的一部分重建。注意“Canvas重建”以Canvas为单位同一个Canvas下任何一个UI元素的改动都可能牵连整块Canvas的批处理数据。所以我之前在3.2节推荐的“置顶独立Canvas”方案不只为了排序清晰更是为了让动效UI的频繁更新不会拖累静态主UI。实际项目中要把频繁变动的飘字、加载圈、滚动列表放在单独的Canvas中静态背景、按钮、大段文字放在另一个Canvas中让Canvas在帧间只重建它需要重建的那部分。Unity的UI Profiler面板里可以看到每个Canvas的重建耗时排序做久了以后打开性能面板第一件事不是看DrawCall而是看有哪些Canvas在疯狂重建。5.3 动静态UI分离与批处理优化批处理的意思是Unity将相邻的UI元素合并到一个DrawCall里绘制。相邻的前提有两个绘制顺序连续而且材质兼容。所以调整Hierarchy顺序不只是改遮挡还会直接影响批处理。很多团队会在搭建界面时把所有Image放在一起、Text放在一起、按钮放在一起理由是图集分类方便。但从批处理角度看这反而打断了“同一材质相邻绘制”的条件让引擎频繁切换材质状态产生更多DrawCall。更合理的组合是将同一个图集里的Image、Text、Button按视觉遮挡关系排布让它们尽可能连续绘制。这里还要提一下图集的作用。DrawCall优化的最大前提是共享图集如果每张图片都来自不同TextureUnity再怎么批处理也没用。项目里应当规划好UI图集把同一个面板的素材打进同一张图集里。然后再通过Hierarchy顺序控制UI排序而不是为了排版美观把相同类型的组件强行归类到一起。6. 常见问题与排查技巧实录每次讲完排序原理还是会有人在群里发一堆各种奇怪截图来问。我这边把这些年高频踩到的几个问题汇总一下直接给排查路径。6.1 UI被3D物体遮挡或3D物体穿透UI如果出现3D物体盖在Overlay模式的UI上面首先确认UICanvas的Render Mode是不是还在Overlay别不小心把它改成了World Space或者Screen Space Camera。Second检查相机列表看看是不是有第二台相机Depth值很大并且Clear Flags和Culling Mask设置得不合理。另外如果项目里用过URP或HDRP还要确认Render Objects的覆盖设置有没有把UI相机覆盖掉。3D物体穿透UI的另一个常见来源是某些特效Shader里加了深度写入。这些特效虽然跑在透明渲染队列但因为写了深度缓冲导致后续UI绘制时深度测试失败UI就被“穿透”了。排查方法是逐个关闭特效找到肇事特效后去掉Shader里的ZWrite阶段或者在特效材质上关闭深度写入。6.2 粒子特效在UI之间“消失”或乱序粒子消失通常是渲染队列问题。UI材质用的是透明队列3000粒子材质也大多在透明队列如果两边的RenderQueue一致Unity在这个队列里再去比较深度和排序。但UGUI Canvas和粒子系统分属两套排序系统同一个Sorting Layer下偶尔会冒出奇怪结果。我总结的做法是UI粒子必须给独立的Sorting Layer放在一个专用特效层中让它和普通UI、普通3D物体彻底分离。再配合透明队列的深度测试就能稳定控制。如果粒子还乱序查看粒子系统里的Render Alignment、Sorting Fudge参数把Order in Layer和Sorting Fudge配合设定效果会比较稳定。6.3 按钮点击区域与实际视觉不一致排序问题和点击区域在GraphicRaycaster里是同一套顺序逻辑。GraphicRaycaster在对UI元素做命中测试时会按Canvas内的绘制顺序从后往前遍历后绘制的元素优先被判断命中。也就是说一个看不见的透明Image如果在排序上位于按钮上方它会先拦截这次点击按钮就变得“点了没反应”。这时可以从两个方向处理一是调整透明遮罩的层级让它别挡住按钮或者不要让它参与射线检测在Image上关闭Raycast Target二是通过代码扩大按钮的点击范围给按钮的Image单独写一个自定义类重写OnRectTransformDimensionsChange后扩展一个点击判定矩形而不是在上层叠一个大而无形的碰撞体。6.4 用Frame Debugger和UI Profiler定位排序问题最后还是要推荐两个工具它们比人肉猜原因可靠得多。Frame Debugger能看到每一条渲染命令包括当前DrawCall使用的是哪个材质、哪个网格、排序顺序是多少。定位“一个UI为什么被另一个UI盖住”时直接在Frame Debugger里找到这两个元素的DrawCall对比它们的渲染顺序立马知道谁先谁后。UI Profiler则重点看Canvas重建情况它能列出每个Canvas的Batch、DrawCall、重建耗时。如果某个Canvas的重建耗时特别高就说明这个Canvas内部动得太频繁需要按第5节的方法做动静分离。这两个工具搭配使用大部分UI渲染问题都能在几分钟内锁定根因。我平时排查流程是先看UI Profiler确认卡顿和重建集中在哪个Canvas再用Frame Debugger精确定位DrawCall顺序最后根据排序规则调整Hierarchy或Sort Order。最后提一个工程习惯所有UI排序相关的配置统一由项目里一两个人维护不要今天这个同事建一个Canvas设个Sort Order明天另一个同事再改一下Sorting Layer。散乱的排序配置虽然短时间能解决局部遮挡但会为后续埋下大量坑。把Canvas分层、Sort Order范围、特效层规则都写成一份简短文档让团队都按这套规范来比任何高级技巧都管用。
返回列表