• 【转】UGUI VS NGUI


    原文:http://gad.qq.com/college/articledetail/7191053

    注[1]:该比较是基于15年-16年期间使用NGUI(3.8.0版本)与UGUI(4.6.9版本)所得

    注[2]:仅对工作中经常接触到的功能做总结,如有疏漏,欢迎指正讨论

    渊源

    先来段小八卦,听说UGUI的主创人员是从NGUI招过去的,所以,UGUI中有很多概念,对于用过NGUI的童鞋来说,看起来都似曾相识。

    先来个概念对比:

      NGUI UGUI
    锚点 Anchor RectTransform Anchor
    图片 Sprite Image
    文字 Label Text
    根节点 UIRoot Canvas
    UI面板 Panel Canvas
    UI容器 uiWidget Panel
    事件交互 Collider EventSystem
    单张贴图 Texture RawImage
    UI相机 camera + UICamera camera + EventSystem

    再看下UI主要的通用几个组件:

      NGUI UGUI
    Button
    Toggle
    Scroll Bar
    Progress Bar
    Input Field
    Popup List ×
    Localization ×
    Play Sound ×
    Scroll View

    总体来说,对于基本的UI元素, NGUI与UGUI都有;在组件的丰富度上,UGUI略逊于NGUI。例如,UGUI没有Localization、Play Sound组件;NGUI的Label是支持静态图文混排跟文字URL超链接的,而UGUI不支持。所以使用UGUI的时候需要自行开发以上缺失的组件及功能。

    差异化

    RectTransform

    UGUI采用RectTransform的概念,将NGUI中的Transform+Widget+Anchor的功能,封装了起来。

      UGUI NGUI
    Pivot RectTransform Widget
    Width、Height RectTransform Widget
    x、y、z RectTransform Transform
    Anchor RectTransform Anchor
      UGUI VS NGUI UGUI VS NGUI

    相对于NGUI的Anchor可以直接对某个GO进行锚点而言,UGUI新的锚点设计显然只能针对父节点来进行,是做不到跨层级锚点的,丧失了灵活性;但是也解决了NGUI多重锚点之间,由于时序问题,控件“锚飞了”的情况。

    比较难理解的,应该是Anchor按比例锚点做相对布局的情况,普通的使用方法,可以参考Unity的官方文档[1],这里不展开。

    要使用脚本来修改UI控件,情况就会变得比较复杂,需要理解绝对布局(anchor四个角都在一起)跟相对布局(anchor四个角分开,会按父控件的百分比来定义该控件)的情况。同时,也要理解Transform与anchoredPosition的关系。

    曾经碰到过一个问题,UI偶现消失。后来发现是因为开始创建的时候没有对Localposition做重置,UGUI会通过anchoredPosition来重置Transform的x跟y,但没有重置z值,所以z值是乱的,所以当Z值大于UICamera的Clip范围的时候,就会被裁剪掉,消失了。

    层级管理概念

    UGUI采用Hierarchy排序的方式,替代了NGUI中的Depth排序。

    更精准的说,NGUI的排序是通过Depth、Z值、RenderQueue共同影响的,整体规则过于复杂;而UGUI采用的排序比较简单,在Canvas内部元素采用Hierarchy方式排序,在Canvas同级之间通过Sort Order或者是Hierarchy来进行排序;UI的组织方式,就是层级的可见顺序,跟平常的认知具有一致性。

    3DMesh嵌套问题

    通常UI开发中都会存在UI与Unity3DMesh嵌套的需求,特别常见的是角色与粒子特效。通常有两种方案来处理嵌套。第一种方案是将3DMesh渲染到一张RenderTexture中,再用这张RenderTexture来与其他的UI做排序。这种方案在UGUI与NGUI中都可用,比较适合角色这种渲染目标唯一并使用控件唯一的需求。

    另一种方案是将3DMesh的渲染与UI一起排序,UGUI跟NGUI都没有提供将Unity3DMesh内嵌为一个UI元素进行排序的功能。

    在NGUI中,官方论坛推荐使用一个脚本来动态修改Mesh的RenderQueue,达到跟UI排序的目的。具体逻辑是每帧获取目标UI的RenderQueue,同步更改Mesh的RenderQueue。因为NGUI默认的排序方式,是每个drawcall对应一个3000以上的RenderQueue。

    在UGUI中,采用的是Sorting Order的方式来做排序,需要修改的是粒子Renderer的Sorting Order来跟UI进行排序。所以使用Sorting Order来管理canvas之间的层级关系时,需要留出足够的Order厚度来支持Canvas内部元素嵌套使用粒子特效。

    Mask裁剪

    NGUI中的Mask裁剪,是通过透明度裁剪实现的;UGUI中的Mask裁剪,是通过Stancil裁剪实现的。透明度裁剪,需要在shader中做额外的透明度因子计算,并且被裁剪掉的像素(透明度为0的像素)还是会参与到后续的Blending等渲染流程;Stancil裁剪,需要额外一个pass来渲染mask区域,但没通过Stencil的像素会被丢弃,不参与后续渲染流程。权衡下来,UGUI的做法会更省一点。特别是被裁剪区域特别大,或者区域内重叠框体特别多的情况。

    3DMesh裁剪

    在UI的mask组件内部层级嵌套使用3DMesh时,粒子也需要被裁剪。NGUI与UGUI都需要对嵌套的3DMesh做裁剪处理。

    图集管理模式

    UGUI不再使用自定义Atlas资源,image中使用的是Sprite,图集可以用Unity提供的Sprite Picker来合成。

    NGUI抛弃了Unity的Sprite组件,自定义了一套图集资源及管理模式(Atlas),提供合图工具、Sprite选取窗口、并提供了对单个资源做静态深加工(加阴影、轮廓)的功能。

    UGUI VS NGUI UGUI VS NGUI

    UGUI整合了Unity的Sprite,使用Sprite Picker来做合图集,功能上简化了许多,但可以直接从Unity的Project窗口中直接把Sprite或者Multiple sprite中的某个Sprite直接拖到Image中,操作上简化了些。

    对于小型游戏UI来说,UGUI的简化版功能会更便捷些。但是对于MMORPG这种有复杂UI层级的游戏来说,使用UGUI的自动合图集,由于可控度太低,大概率会造成内存浪费,或者Drawcall Issue。所以,复杂UI层级的游戏,使用NGUI的图集管理模式,可控度会更高些。在UGUI中,可以通过使用第三方工具(如TexturePicker)合图后转成Multiple Sprite来达到跟NGUI相似的可控度。

    采用Multiple Sprite有个缺点,不能直接动态加载这个MultipleSprite中包含的某个Sprite,需要使用LoadAll接口将该图集所有的Sprite都加载以后,再遍历对比出所需的Sprite。而Unity的LoadAll函数,是会对Resource中所有文件进行遍历的。当Resource中的文件数量非常庞大的时候,遍历耗时会造成性能卡顿。

    这个问题,可以通过将自定义Altas中的Sprite导出成Prefab,并提供一个Sprite Prefab的加载缓存管理器即可。除此之外,精简Resource中的文件数量也是一种很好的方案。“Best Practices for the Resources System —— Don't use it”。[2]

    渲染性能

    Batch算法

    NGUI的Panel内部排序:在Widget新增时,是通过Insert到List中进行排序的,整个算法的时间复杂度是O(n),元素越多,效率越差。在Rebuild时,直接遍历WidgetList,按材质、贴图、shader来拆drawcall,进行batch。

    UGUI的canvas内部排序:由于没有明确的指定次序,UGUI需要按照顺序,对每个UI控件遍历出相交情况,再查看是否可以合批,算法比NGUI更复杂些(判断相交的算法有优化,时间复杂度小于O(n2))。

    Native Code 优势

    然而,NGUI实质上是创建自定义Mesh,使用Mesh Renderer来做渲染的。对UI的合批操作逻辑在C#层。UGUI定义了Canvas组件,使用Canvas Renderer来做渲染,在C++层做合批逻辑,所以UGUI处理动态层的性能会比NGUI更佳。

    对于有频繁动画需求的UI,使用UGUI会比使用NGUI有性能优势。

    EventSystem & Raycasters

    UGUI中采用Event&Raycasters的设计,来替代NGUI中的Collider&sendMessage。

    在NGUI中,默认控件是不参与交互的,除非加上Collider;而在UGUI中,默认控件是参与交互的,除非使用canvas Group组件来禁止交互(在5.x版本中,可以通过去掉勾选raycast Target来禁止)。所以在使用UGUI的过程中,突然发现某个点击无效了,可以优先考虑是否是最近有人添加新的Text把消息遮挡了。

    NGUI的交互事件是通过SendMessage来发送消息的,SendMessage的性能比较差,Delegate与SendMessage的性能测算,前者会快10倍左右[3]。UGUI的交互事件改为通过事件回调机制来发送消息,对于交互输入这种比较频繁调用的消息来说,性能上提升不少,比较直接反馈是scroll view的滑动流畅度。

    Layout布局

    UGUI的布局,采用Layout组件群(Layout Element、GridLayoutGroup、Content Size Fitter等),来替代NGUI中的Table跟Grid。

    对于UGUI来说,Layout是比较费的组件,因为每次被标记Dirty时,它需要重算所有的子元素的大小跟位置。一般来说,只用在跟内容的相对大小相对位置有关联的复杂布局中。例如说scrollview中动态添加新Item或者是文本背景纹理需要根据文本的长宽动态变化的时候[4]。

    平时可以在编辑模式下,用Layout组件来辅助界面快速布局。另外比较小规模的相对布局(如2*2table),可以使用RectTransform的Anchor替代。因为RectTransform的计算是在C++层发生的,比同等的Layout更效率

    UI动画

    NGUI整合了ITween,并将ITween的使用封装成脚本,可以非常方便制作出各种旋转、平移、缩放的效果,易用性很强。

    UGUI官方文档建议是采用Unity的Animation System来制作UI动画[5],但是使用Animation动画有个比较明显的缺陷,在UI频繁显隐的时候(Active/Inactive),Animator会重新Rebind一次Controller,导致无意义的性能损耗,所以UGUI的动画实现,可以有另一个option,通过整合DoTween来实现。Dotween不存在Animation的反复初始化问题,并且它使用了一些缓存策略,相对于ITween来说,每帧耗时更短,效率更高,产生GC更少。[6]

    总结

    从易用性角度来看,NGUI比UGUI好用一点点

    NGUI的工具支持度更高:

    Drawcall Tool,可以查看UI的Drawcall分布以及哪些元素影响了Drawcall分布,可以很方便的调整顺序优化Drawcall;

    Prefab Toolbar,可以将Prefab模板直接保存成可视态,通过拖曳到Hierarchy创建新组合;

    Editor Right - Click Menu, 在编辑场景中,右键弹出的操作菜单,可以在多重叠控件中,拾取某一个,或者对某个控件进行层级操作。在开发多层级复杂UI的时候,这个功能是可以提升效率的。

    ITween, NGUI整合了ITween并提供了封装后的使用脚本,可以很方便快捷的完成UI的动画需求,如旋转、缩放、变色、移动等。

    UGUI的概念封装更好,可视化更高:

    将层级管理改为Hierarchy排序以后,比较符合人类认知;

    对于可交互空间的navigation顺序,可以直接给出可视图;

    深度利用Unity的内核,如Inspector,Sprite等;

    从可扩展性来看,UGUI略逊NGUI一点点

    UGUI与NGUI都可以通过简单组件组合,来满足大多数的需求;也可以通过继承扩展现有组件来满足特殊需求;

    NGUI更极端一点,可以通过直接修改源码来满足需求。UGUI虽然也开放了源码,但只是C#部分,C++部分是无法做改动的。

    从性能来看,UGUI比NGUI有底层优势

    UGUI的排序及合批逻辑都在C++层处理,效率更高;

    采用EventSystem的分发模式,效率更高;

    结论

    除以上总结外,UGUI还有几个优势:全免费、官方身份、跟Unity可深度融合(多线程)等。

    选用UGUI应该是趋势,因为性能是关键, 易用性可以通过开发辅助工具或者扩展组件来弥补。随着使用者越来越多,UGUI会越来越强大。

    Reference

    [1]  Unity Manual - UI - Base Layout

    [2]  Best Practices -- The Resources folder

    [3]  Unity3D的Delegate和SendMessage的性能差测试

    [4]  UGUI中实现底图自适应size的方法

    [5]  Unity Manual - UI - Animation Integration

    [6]  Unity CPU占用-DoTween与iTween效率对比

  • 相关阅读:
    java之 向上转型与向下转型
    java之 惰性初始化
    mysql 学习网站
    history of program atan2(y,x)和pow(x,y)
    atom 之 前端必备插件
    js之正则表达式
    Django之模型层
    每日作业5/28
    每日作业5/27
    每日作业5.26
  • 原文地址:https://www.cnblogs.com/mimime/p/6532990.html
Copyright © 2020-2023  润新知