美文网首页
[UGUI]Unity工程中的UI优化和坑

[UGUI]Unity工程中的UI优化和坑

作者: 想变的很强的日向彻 | 来源:发表于2019-10-18 15:33 被阅读0次

    1.UI管理设计模式

    使用工厂模式创建window,通过UI名字索引进行动态创建,降低耦合性,至于gameobject所绑定的类,可以选择使用addcomponent添加,也可以在预制体上直接绑定好,这两种方法的区别是AddComponent可能会有小的开销,比起直接将代码放在预制体上面。

    步骤为:加载对应名字的预制体,通过GetOrAddComponent为UI添加对应名字的类并且Init初始化,直接add或者通过反射获取类

               (1)window = winGo.GetComponent<IUGUI>() as IUGUI;

                (2)System.Type tp = System.Type.GetType("UITest");

                if (tp != null)

                {

                    window = System.Activator.CreateInstance(tp) as IUGUI;

                }

    2.ui的加载,同步还是异步?

    同步加载的好处:直接打开UI,将逻辑在一帧内执行完毕,对于不是特别庞大的UI感觉不到卡顿的话,玩家的体验也比较好,UI瞬间打开,Cpu得到充分利用

    弊端:对于比较庞大的UI打开可能会引起掉帧卡顿,尤其在预制体很大的情况下

    异步加载的好处:异步加载的思路是将加载的过程分帧进行,好处就是在UI打开的时候不会引起当前帧的线程处理速度而引起掉帧卡顿,那么关键就在于我们的加载过程如果用协程,yield return来分割的话,在哪几个节点进行分割最能平滑和高效的分割这个过程,而卡顿的元凶则是预制体prefab的加载,而一般工程都会有异步加载资源的机制,所以将prefab的加载设置为异步加载,待加载完成后再进行后续逻辑可有效的缓解卡顿

    而异步加载同样也存在弊端,所有的异步加载都可能存在的问题就是Cpu得不到充分利用,或者说分帧不合理不均匀适得其反,拿刚进入游戏的Loading来说,很多做法是异步加载,但其实从逻辑上分析来说,同步加载更好些,为什么呢,异步加载每帧的cpu大多数是得不到充分利用的,也就是说33ms的时间,20ms在加载,剩下13ms在等待,而同步加载每帧的时间都得到充分利用,而loading这种特殊的情况下,本来界面就是进度条状态,玩家也是等待,同步并不会造成卡顿上的影响,反而整体应该比异步更快

    3.UI中的Mesh

    UI中的Mesh重建过程会带来大量的开销,那么如何避免和减少Mesh重建的可能呢

    (1)当ui发生改变,比如有组件的添加、删除、遮挡关系改变等事情发生的时候会需要重建Mesh,而当ui的transform发生改变的时候,如果不影响原本的遮挡关系,是不会导致Mesh重建的过程的。

    Tips:减少Mesh的重建次数,创建或者销毁,还有active和deactive的过程都会导致Mesh重建,推荐通过Layer对相机的层进行隐藏,或者将其移除视口外

    (2)UGUI中Mesh的重建的基本单位是Canvas

    Tips:减少Mesh重建时的影响范围,动静分离,动态变化的部分每帧都会引起Mesh重建,可以选择将动态的部分合并,并且添加一个Canvas组件

    4.慎用UI的outLine和shadow

    (1)OutLine

    OutLine的描边会额外生成四份Text文本,而每个文本是一个矩形,两个三角形,这会增加大量的顶点,可以通过将OutLine组件的Effect Distance调大看到周围多了四个文本,这就是产生的额外开下,如果我们有大量的Text而他们都添加了这个组件,可能会导致顶点剧增甚至引起崩溃

    (2)Shadow

    Shadow和OutLine很类似,但好一点的是Shadow会额外产生两个,但是这在数量巨大的时候也同样要小心

    5.防止预制体过大或者频繁加载和卸载

    (1)每个预制体不应该过大,如果超过了一定的大小,应该对其进行拆分,保证单个预制体不至于过大,不然在加载资源的时候难免会遇到卡顿和掉帧的情况

    (2)页签间的Prefab切换应该进行缓存,在第一次访问时加载(或者界面打开的时候都加载进来,需要进行分帧),然后再代码中进行缓存,下次访问直接使用

    6.同一个Layer的UI要尽可能不要重叠

    7.Button的射线检测实际上是通过绑定在Button上Image组件来判断是否点击的,同样如果Image不需要进行事件监听,则应该把其RayCast Target取消勾选


    8.如何隐藏画布

    通过禁用Canvas而不是setactive(false)来隐藏,禁用Canvas组件会阻止画布向GPU发起绘图调用,所以该画布不再可见。然而,此时该画布不会丢弃它的顶点缓冲区,它会保留所有网格和顶点,当重新启用时不会触发重构过程,它只会重新绘制画布内容

    1.分帧的地方 2.逻辑线程帧的原理

    9.UI继承关系分析

    (1)UIBehavior

    UI的基类

    可以看到 UIBeahavior中的大多数方法都是沿用的MonoBehavior的方法(绿字),而要证明调用的到底是父类MonoBehavior中的这个方法还是子类的方法,可以新写一个类,观察继承自MonoBehavior和UIBehavior的情况差别,看是调用的父类还是子类的。(继承自mono可以监听到,说明子类UIBehavior做了其他的工作而不是监听到的父类事件)。

    tips:Mono的组织原理,On类的方法多是作为框架的回调,比如UI父节点中发生变化时,调用父节点下面所有UI节点的OnCanvasChange方法(此处可以应用脏标记,不发生变化的不用处理)

    (2)Graphic

    图形基类

    继承自UI基类和一个接口ICanvaseElement(名字可以看出来时UI中的基本单元),UIBehavior上面已经分析过了,下面分析一下这个接口和Graphic

    ICanvasElement:

    ICanvasElement

    interface 1:父类中Component有一个Transform,也就是我们平时可以Get到的Transform方法,所以这个接口中的方法,应该是用来计算UI系统中的位置而产生的额外的变量

    interface 2: 图形构建完的回调

    interface3:布局完成的回调

    interface 4:Canvas重建完成的回调(这也解释了为什么UI优化时提到的UI重建的基本单元是Canvas,而一个Canvas管理着他下面的ICanvasElement节点,所以要减少UI的重建以达到减少这些调用和重建子节点的开销)

    Graphic

    看到很多脏标记回调(一种优化策略)

    这个层级顾名思义,主要负责图像的生成,点击检测(raycastTarget),纹理, 材质,颜色, 透明度处理,再就是很多图像生成,混合,处理后的回调

    (4)MaskableGraphic

    MaskableGraphic

    基本完全继承了Graphic,只不过多加了Mask这层功能,可以看到,后面三个新的接口也基本上是服务于这个新的mask功能

    (5)对比Image和Button

    Image的定义 Button的定义

    对比来看,Image父类中具有图形相关的功能,包括点击事件的监听,而Button中基本上都是IPoint开头的接口,也就是用于实现事件系统的回调,而Selectable中有一个变量public Graphic targetGraphic,也就是他需要检测的点击事件,一般会绑定一个Image,那么为什么要绑定一个Image呢,原因肯定就是Button自己本身不具有这个功能呗,将这个功能放在了Image里面,所以Button的点击其实是通过Image,而Button的这个变量即使不绑定任何东西,只要子节点有Image就可以监听到消息(可以自己尝试)。

    10.自己实现UI组件(比如曲线滑动列表)

    踩过这方面的一些坑,简单说下思路把,继承UIBehavior,外加一些接口,类似于IDragHandler等,OnBeginDrag(PointEventData eventData)

    底层相应的数据都在eventData里面,自己做处理,通过鼠标移动的坐标做一些自己组件的计算

    相关文章

      网友评论

          本文标题:[UGUI]Unity工程中的UI优化和坑

          本文链接:https://www.haomeiwen.com/subject/gfuymctx.html