在我们项目开始用ugui做UI开始就一直有在显示或者隐藏(用的是gameobject的setactive接口)UI的时候会产生gc以及一些计算,这个主要是unity的maskablegraphic在onenable的时候做了graphic的rebuild以及layout的rebuild,在ondisable的时候也做了一些计算。
刚开始有用把整个UI界面移出屏幕的方法来避免显示隐藏的消耗,但是dc还会在哦。还用了一种把UI的scale设为0,0,0。dc是消掉了,但是如果UI里面有粒子系统,某些粒子系统是不受scale影响的。还可能会影响UI里面的tween动画。显然这两种方式并不是很好用。
今天改了ugui的底层,把maskablegraphic类里面的updatemodifiedmaterial虚函数重写掉,它的子类并没有重写这个函数,所以我们直接派生相关的UI组件,并重写它,里面主要有一行代码会去获取mask组件,这个是很耗的。通过缓存mask组件,可以避免这个消耗,同时预留一个setmask接口,避免后面有添加mask组件时,要调用这个接口。这样了弄了之后,显示隐藏UI的gc下降了2/3。但是仍然还有一些消耗。
通过在UI的根节点添加cavas组件,用这个组件的enable代替gameobject的setactive接口,显示以及隐藏UI变成0消耗。至此,这个问题完美解决!
当使用canvas显示隐藏UI的时候,底下的非UI元素(特效,如meshrender,particlesystem)不受控制。通过在特效根节点加一个组件,继承UIbehaviour,重写oncanvashirerchychanged接口,当UI根节点的canvas状态发生改变时会回调这个函数。我们在这个函数控制特效子对象。对于meshrenderer,我们对组件的enabled进行设置,particlesystem,我们用play以及stop进行控制,还有clear接口。
当查看显示隐藏UI的gc消耗的时候发现有4个lua回调c#的接口gc非常高,通过导出c#类到lua解决。
当canvas下面还有canvas并且勾选了overridesorting,底下canvas就不受顶部canvas控制。这个时候就不适合用canvas模式来控制显示隐藏。
原先对mask的缓存是在awake获取,但是当UI对象很多并且大部分没有mask组件的时候,会造成不必要的性能损耗。因此最后我们把mask变成public字段,并在editor显示它,然后写个工具把所有mask获取到并缓存起来。同时要注意当后面动态加了mask组件,要自己设置引用,这个可以通过封装add mask组件接口来实现。