Unity3D UGUI 优化:Font.CacheFontForText

前言:

最近在做项目优化的时候,有个东西非常耗时,就是Font.CacheFontForText。在真机上测出来的Profile简直吓人,几十、几百、上千毫秒都有可能。

所以这是一个必须优化的点,也是一个很难的点。因为他的API等东西,都是写在原生的Unity中的,外部访问不到;而如果自己重新实现一套又太费时费力了。

 

正文:

1、现状分析:

上图可以看到,由font.cachefontfortext 引起的消耗已经有30ms以上,令人发指,而且这还属于在 电脑上跑,性能比较好的情况。至于手机,想想看就知道会惨不忍睹。

至于为什么会这样,引用一篇文章(https://blog.csdn.net/jackianroy/article/details/53890804)的说法:

CacheFontForText是在UILabel在渲染时,在对应的字体的FontTexture找不到需要渲染的字符时,就会重新生成FontTexture,FontTexture尺寸越大,重新生成这张FontTexture就越耗时。

虽然不是很确定这个说法是否是正确的,但是确实和实际情况对的上。因为看Profile里确实是从TextureRebuilt中开始调用,从而导致了很多Text的重绘,从而导致卡顿。其实我们再往内部查看,其实可以看到,其耗时的原因是因为FontTexture重新绘制之后,所有依赖这个字体的Text就会把自己进行Rebuild,从而造成很多Text的重绘,造成卡顿。

 

2、关于Font设置的Dynamic 和Unicode

这里说的是字体在属性面板中的字体设置,先贴一下Unity手册中的说明:

https://docs.unity3d.com/Manual/class-Font.html

Import Settings for a font

看到图中的Character属性被设置成了Dynamic,我们也可以将其选择为Unicode。这两者运行起来的区别其实也没有特别的说明,不过就我的经验来看有两点:

设置为Unicode时,FontSize最好设置大一些。如果像图中设置为16,那么在Text中,如果FontSize大于16时就会显得模糊。而使用Dynamic则没有这个问题,你的字体永远是最清晰的版本。

设置为Dynamic的时候,在编辑器下(Win10)不会触发字体重绘(Font.CacheForText)这一套操作,或者说很难触发。有的说法是因为操作系统做了优化,但实际上并没有官方的解释。这里说的不会触发并不是说当设置成Dynamic的时候,Font.CacheForFont就一定不会被调用,而是说在真机上发生字体贴图重建的节点在编辑器下不会发生。这对于优化来讲是个很严重的问题:我明明在编辑器明明没有,为什么一到手机上就贴图重建了呢?而设置为Unicode的时候,编辑器下在同一节点也会触发CacheForText,这样至少在调试的时候就方便了许多。

 

3、解决思路

其实解决方案可以说是几乎没有。有一篇国外论坛上说的方法,就是一开始把所有的可能的字都交由Text取绘制一遍,之后便可不再继续绘制了。然而现实是残酷的:英文只有几十个字母,加上数字和标点符号,总共也就一百来个字符而已。但是中文就不一样了,字的数量非常多,而且都是不重复的。所以要想一次绘制完所有的字符,很困难。就算真的全绘制出来了,不但会在内存中产生一个很大的贴图,而且还又很多的冗余。

这个时候我们只有通过对文本、字体的使用来优化了。

Font类中有一个很重要的回调方法需要监听:

public sealed class Font : Object
{
    //……
    public static event Action<Font> textureRebuilt;
    //……
}

在UnityEngine中的Font类中,有个事件:textureRebuilt 。我们可以通过监听这个事件,来知道哪些字体发生了重绘,从而对其进行优化。

 

4、优化方案

在 调试过程中,我发现textureRebuilt的发生总是发生在这样一种情况:某个面板上用了之前没有使用、或虽然使用过但字数很少的字体,当他第一次打开的时候,就发生了重绘。

例如,我的主界面等大部分都是用的宋体,但其中某个界面用的微软雅黑。在打开这个面板之前,雅黑这个字体基本没用过,或者用过,但只有几个字符。当我打开这个面板的时候,由于需要大量的雅黑字体,此时就发生了textureRebuilt。

之后,我将这个面板中的所有字体都修改为宋体,再次打开的时候就不再发生字体贴图重绘了。而当我的Font设置为Unicode的时候,编辑器和手机基本上是一致的。编辑器下没有重绘的情况,手机上也不会发生。

 

5、其他问题:

现在解决了动态重绘的问题,难道就是完美的解决方案了吗?显然不是的,如果这个方式完美,就不会有动态字体的产生了。这样把字体设置成非静态模式,问题之一就是会占用极大的内存。随便一个字体就占用了32mb的内存,初始字体设置得越大,可能占用也会提升。但是初始字体设置得较小,字体就会不清晰。这样在内存上会产生很多的冗余(毕竟不是所有的字都用到了),但是总体来讲提升了CPU耗时。

除了内存问题,不使用动态字体的第二个问题就是会导致富文本中的字体大小调整失效。只是改文字的颜色(<color=#ecd781> </color>)是不会有问题的,但是如果改了文本的字号(<size=36>   </size>),就会导致报错:

Font 'XXXXX (UnityEngine.Font)' is not dynamic, which is required to override its size

所以如果不使用动态字体,就不能使用富文本的方式改文字的Size了。

 

后记:

其实我并没真正解决掉这个Font.CacheFontForText造成的性能问题,我只是将其规避。如果出现了某种未知情况,或者项目的需求让我无法规避的时候,还是会产生同样的问题。

在优化过程中,我意识到,我们不能随意使用字体。最好的方案是,整个项目都是用同一种字体,从头到尾。如果各个面板都是不同的字体,那么不但很占用空间(尤其是中文),而且还很容易发生字体重绘导致的性能问题。所以关于这一点,一定要和美术沟通好。

 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值