Unity UGUI优化:解决EventSystem耗时过长的问题 第三部分

前言

之前写了两部分关于EventSystem的优化,但是后面项目需求上还不够,还要继续(毕竟优化没有止境)。所以今天开始第三次对EventSystem的优化。

本次优化的是在前两部分的前提下开展的,所以如果没有进行过前两部分的优化需要补一下(前两部分的效果最明显也最关键,这一次是更进一步)。

第一部分:https://blog.csdn.net/cyf649669121/article/details/83661023

第二部分:https://blog.csdn.net/cyf649669121/article/details/83785539

 

正文

1、屏幕内的判定

在实际开发过程中,为了优化界面的打开速度,会把一些界面提前制作出来然后隐藏在屏幕外。这种UGUI开发的常规操作却有一个隐患:那就是在点击事件的判定过程中,会把所有的Canvas都拉进去判定(因为没有SetActive为False)。也就是说,你所有的UI元素都会走一遍GraphicRaycast 的 Raycast 判定。虽然在前两部分中,我们减少了一大批不需要进行判定的部分,但实际上还是有很多界面外的元素做了额外判定。

所以这里我们就需要一个函数来判定某个Canvas是否在屏幕内(因为点击事件是以Canvas为单位进行分组),如果他不在屏幕内,就跳过这个Canvas:

        public bool _IsOutScreen = false;
        /// 
        /// 是否在频幕外;
        /// 
        public bool IsOutScreen
        {
            get
            {
                bool isOut = false;
                var pos = eventCamera.WorldToViewportPoint(transform.position);
                if (pos.x < 0 || pos.y < 0 || pos.x > 1 || pos.y > 1)
                    isOut = true;
                _IsOutScreen = isOut;
                return _IsOutScreen;
            }
        }

这个判定的具体结果如下:

如图四种情况,其中白色半透明部分是摄像机的视角范围, 白色方块是某个Panel,红色方块是白色方块的一个子对象。现在针对白色方块,四种情况的判定如下:

A:屏幕内;

B:屏幕内;

C:屏幕外;

D:屏幕外(但是红色方块在屏幕内);

总结一下:只要白色方块只要有一丝丝在摄像机里,就会被判定为在屏幕内,只有白色方块完全脱离摄像机才会被判定为在屏幕外。而白色方块的子对象(红色方块)是否在屏幕内,不影响其判定。

根据此原理,我们制作UI的时候,需要将子对象的设置在父对象的Canvas内,除非子对象在屏幕外的时候不需要触发EventSystem。

 

2、只对屏幕内的UI做EventSystem判定

这是没有优化前的情况,可以看到在RayCast2中有200多个元素在进行各种点击事件的遍历。

实际上很多UI元素并不在屏幕内,所以整个Canvas是可以跳过的不需要判定的。

接下来我们在RayCast里面对是否在屏幕内进行判定,如果不在则跳过这个Canvas:

public override void Raycast(PointerEventData eventData, List resultAppendList)
{            
    if (canvas == null)return;
    //如果本Canvas在频幕外了,也不需要进行判定;
    if (IsOutScreen) return;
    ……
    ……
    ……
}

这样的效果如何呢?如下:

可以看到,需要判定的UI一下子减少到了只剩80个,效果还是不错的。从耗时上来看,比之前的1.75ms减少至0.6ms,快了1ms。

 

3、对 IsOutScreen 方法进行优化

关于新加的 IsOutScreen 方法,本身的性能消耗如何呢?如下图:

我的这个测试 15 此调用,总共耗时 0.8~0.11 ms 不等。看起来他的消耗来自于获取相机、坐标转换部分。我后来想了一下,如果这个元素和上一次计算时没有移动过,岂不是就不要再进行计算了?根据这个思路我又写了一版:


        /// 
        /// 上一次检查的位置;
        /// 
        Vector3 mLastCheckPostion = Vector3.zero;

        public bool _IsOutScreen = false;
        /// 
        /// 是否在频幕外;
        /// 
        public bool IsOutScreen
        {
            get
            {
                var curPos = transform.position;
                if (curPos.x != mLastCheckPostion.x || curPos.y != mLastCheckPostion.y || curPos.z != mLastCheckPostion.z)
                {
                    mLastCheckPostion = curPos;
                    CheckOutScreenMethod(curPos);
                }
                return _IsOutScreen;
            }
        }

        /// 
        /// 是否在屏幕外的具体检测方法;
        /// 
        void CheckOutScreenMethod(Vector3 curPos)
        {
            bool isOut = false;
            var pos = eventCamera.WorldToViewportPoint(curPos);
            if (pos.x < 0 || pos.y < 0 || pos.x > 1 || pos.y > 1)
                isOut = true;
            _IsOutScreen = isOut;
        }

这样下来耗时如下:

最后结果是在0.3ms,优化了0.5~0.9ms……

其实感觉没啥必要啊,各位看自己喜好吧。当然如果有的人GraphicRaycaster 特别多的话,那这个效率这么还是比原来那个快的。

 

 

后记

这次针对屏幕内的优化其实很简单,之前并没有想到。最开始的一版其实没有那么多UI需要预加载出来,所以感觉判定的数量还比较合理,后来随着性能要求的提升,预加载的UI越来越多,这个问题才暴露出来。

当然这次优化显然不是最后一次,后面还要继续加油(虽然我个人觉得差不多了,要知道原来的UI元素只有这个的三分之一左右,耗时差不多在8~10 ms,现在只有 1~2 ms 。然而还是要被要求继续优化……)。

 

 

前端网页加载渲染链路优化

opensignal官方提供了2018年2月份统计的全世界4G网络覆盖率和通信速率的统计分布图如下,在目前移动互联网的浪潮下,我们要利用好用户终端设备的每个字节的流量。

Unity进阶技巧 - RectTransform详解

前言 最近要做UI,有时候需要在代码中调整改变UI控件的属性,比如位置、大小等,然而在NGUI里面,控制UI控件的位置等属性的是RectTransform这个组件,这个组件继承自Transform组件,却增加许多自己的特性,在不了解这些特性的情况下鲁莽的去使用它,会导致出现很多匪夷所思的问题,而且使用起来也不够得心应手,于是决定研究一下RectTransform到底是如何工作的 你将学得到什...

unity圆圈自动吸附屏幕边缘

效果: 设置如下:

Unity UGUI优化:解决EventSystem耗时过长的问题 第三部分

则跳过这个anvas:public override void aycast(ointerventata eventata, ist<aycastesult> resultppendist) { if (canvas ==