Unity琐碎(3) UGUI 图文混排解决方案和优化

摘要:
我觉得在使用Unity之后,我总能看到混合排的各种解决方案,这只能说明Unity不够体贴。您可以在此处参考着色器中的图片渲染:UwaUGUI表达式系统解决方案直接在着色器中渲染图片。当它没有想到一个好的解决方案来避免在轮廓和阴影中处理图片时,这个解决方案就放弃了。

感觉使用Unity之后总能看到各种各样解决混排的方案,只能说明Unity不够体恤下情啊。这篇文章主要讲一下个人在使用过程中方案选择和优化过程,已做记录。顺便提下,开源很多意味着坑,还是要开实际需求。

1. 方案选择

1 TextMeshPro
Unity 最近公布收购了TextMeshPro并且免费开源给大家使用,估计还需要几个小版本才会完全融合到Unity中或者保持现在的状态。TextMeshPro支持效果丰富,兼容现在UI层级等,性能也可以满足移动端,但是很纠结的是:

  • 现在的版本生成的字体库实在太大了,比较全的汉字字库生成TextMeshPro需要的字库之后已经接近17M,如果考虑到游戏中存在2种字体,估计会超过20M的常驻内存在移动端,这个至少现在还很难接受。
  • 另外一个文字是序列化,20M的序列化数据,移动端受IO限制,读取时间会有点长。这个时间长度我没仔细测试过

基于上面亮点,最后我还是没有采用这种方案,如果Unity考虑融合进来,建议修改下字库的使用方式。

2 文字和图片独立渲染

  • 文字和图片采用layout的方式控制渲染位置,最后会生成大量的Text和Sprite,实时计算位置信息,比如:RichText,这里面最大的问题可能会再CPU端造成不必要的浪费。
  • 文字中留空间,图片再这个空间单独渲染。Text支持富文本的时候会控制间隔,利用这个间隔提供图片位置信息,然后单独渲染图片位置。这种方案Text不需要实时更新,图片(带有动态)需要实时过更新。可以参考这里
  • Shader中渲染图片:Uwa UGUI表情系统解决方案 直接再shader中渲染图片,这个方案对于outline、shadow时避免图片也被处理的问题没想到好的方案,就放弃了。

最后采用了文字富文本保留空间,图片根据位置单独渲染的方案,主要的原因在于性能可控以及现在代码还算比较完善(这里完全是个坑)。这个版本最初的源码:https://code.csdn.net/qq992817263/uguitextpro/tree/master

2. 基本原理

2.1 基本思想

  • 利用Text富文本占位符为图片保留位置、图片名字、长宽等信息,通过字符解析获取图片相关信息
  • 监听Text重绘以及位置更新等事件,并更新图片位置

参考文章
Unity Text 插入图片,这篇文章是基本的实现方式,后面CSDN“神码编程”也就在这基础上做了几处扩展和一些文章分享

神码编程 Unity UGUI 图文混排系列文章

2.2 代码实现思路

  • 提前生成sprite区域信息,如果是一个系列的表情则根据sprite名字进行区分,当然后面也根据名字进行保留和查找。如angry_0angry_1angry_2angry_3 , die_0/die_1/die_2/die_4/die_5/die_6

  • 继承Text组件,重写OnPopulateMesh以及字符解析,维护里面图片位置、顶点等信息

  • 表情管理器:记录所有Text中图片(有效的)位置、纹理、顶点信息的索引关系,由数据变化时生成需要的Mesh信息并提交

  • SpriteAsset 管理器:管理图片中所有Text中使用的图片资源加载以及sprite位置、名字信息。

3. 爬坑记录

最初的源码看似可用,但是在手机端ListView滚动情况下直接掉到20帧一下,即使在静态100个表情同时更新的境况下效率也很难令人满意。所以.................差不多用了一周时间爬各种坑,下面是一些主要的记录:

3.1 优化内容

(1) GC

mark
代码中在解析字符中基本每次都在new数据,包括解析字符、计算图片位置、更新图片Mesh等都存在很严重的GC,看上图就可以看到滚动中如果频繁创建的问题。

优化思路:

  • 对于每个Text,限制最大图片数量以及相关结构数量,只有在不够的时候再进行分配(不超过最大数量则),后续使用中不再进行分配,当然增加了数据有效性判断而不是是否为空。
  • 对于图片管理Mesh,则管理器中图片总数量提前创建,只有再发生变化时才会重新进行内存分配。现在使用的策略还需优化。
(2) 图片信息查找

启动时读取配置信息,并简历sprite名字和信息的对应Dictionary,加快查找。当然也可以直接以Dictionary结构进行序列化,就可以节省这部分空间和时间,待优化。

(3) 有效图片更新方式

原始版本中有效Sprite 列表时通过List的形式进行管理,每次任一个Text的变化(enabled,posotion等)都会将这个列表清除并重新将有效Text中的有效Sprite添加到列表中来。这种方式如果在类似ListView等一直会变化的组件中就会产生不必要的CPU开销。

优化思路:

  • 维护一个有效Text的Dictionary,保存Text中对应Sprite的Key值,在Text OnEnable/OnDisable中进行注册和注销操作
  • 维护一个有效Sprite的Dictionary,保存Sprite string以及实际信息。
  • 每次有Text改变时只修改Sprite 键值表中对应的部分,当然也考虑Text注销等情况。

这种方式避免在频繁更新中不必要的列表清除操作以及对SpriteManager lateUpdate的影响

(4) 图片Mesh数据更新过程时间

最初的版本采用对SpriteList遍历的形式逐个将triangles、uv、vertices 赋值到新创建的缓存中,再扔给iMesh去提交。在ListView快速移动时这部分的时间占用就很夸张了。
mark

优化思路:

  • 尽量减少无效sprite进入列表,限制每个Text中sprite的最大数量
  • 采用Array.Copy的形式替代逐个赋值
(5) 占位符乱码清除方式

原始版本可能时作者计算错误了,清除乱码的UV位置其实只需要向后4个即可,但是也原始版本是按4 * Length(标签长度)来计算,这项的CPU占用率特别高。
mark

(6) 动态表情更新方式

原始版本时在SpriteUpdate中每隔固定时间更新表情的索引(如果有)并重新更新Sprite Mesh内容。会产生一个问题:每种类型表情动画图片的数目不一样,那就很难保证每个动态表情都很自然的播放。提高更新的间隔意味着有些表情像发飙一样

优化思路:
每类型的表情中单独存放其时间间隔以及已经运行的时间,在Update中根据各自的情况进行更新。

(7)图片位置更新方式

原始代码中是在Text :SetVerticesDirty()中进行ParseText的操作并依赖SpriteManager中LaterUpdate更新图片的Mesh数据,产生的问题:

  • SetVerticesDirty 是Text 任何变化都会调用接口,意味着ParseText的操作在ListView滚动过程中一直在进行。
    mark
  • SpriteManager中LaterUpdate更新与Text位置变化不同步,滚动时很明显的可以看到sprite的位置偏移

优化思路:

  • ParseText只在text文本内容变化时进行更新,可通过重载Text的text属性实现
  • 在ListView滚动过程中 sprite变化的只有位置信息,所以只更新位置即可,并且直接更新MESH,不等待SpriteManager。
(8)其他

对应的还有编辑器、数据结构、贴图资源管理等的优化

3.2 新增功能

(1)支持简化标签

支持 "[xxxxx]"来替代冗长的设置

(2)图片层级管理

方便单个Canvas下多个层级,让Text 可以直接设置SpriteManager或者找最近的一个。

(3)增加文本与图片间隔设置

mark

3.3 待优化内容

(1)下划线解析和超链接解析都是基于字符位置对应实际字符顶点位置
(2)字符串解析
(3)图片Mesh
(4)多张sprite Asset

3.4 优化效果

测试方式,屏幕中160个动画表情的情况,在ListView中快速滚动下进行测试的性能曲线(主要时CPU);
优化前
mark
优化后
mark
原生Text, 有占位符,无表情
mark

4 小结

采用这种方案各种原因都有,有好处也有弊端,就像层级问题,解决起来会有点头痛。经过一段时间优化勉强可以在移动端满足需求,不过还有很多可以继续优化的空间。

GITHUB工程文件:https://github.com/carlosCn/Unity-EmojiText
百度网盘资源:http://pan.baidu.com/s/1geZuVNd

欢迎继续补充。

免责声明:文章转载自《Unity琐碎(3) UGUI 图文混排解决方案和优化》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇sql server字符串中怎么添加换行?【205】C#实现远程桌面访问下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

unity3d 导出 Excel

我在unity里需要导出成Excel格式,试了一些方法,其中用c#的com组件的我还没成功不知道该怎么在unity里调用,(如果哪位大哥用别的方法在unity里成功了,可以交流下,最好给我一个小demo(849288321@qq.com),谢谢啦.),不过后来找到了这个org.in2bits.MyXls  ,需要导入这个dll(网上有很多),然后用着还挺好...

Unity3D中Mathf数学运算函数总结

引入: 看到一个案例注意到函数Mathf.SmoothDamp的使用,游戏中用于做相机的缓冲跟踪和boss直升机跟踪士兵。该函数是Unity3D中Mathf数学运算函数中的一个。一些游戏使用了smoothmove的功能,其实就是类似的效果,只是发现这个函数很容易的已经封装好了,查了官网文档发现使用起来真的非常简单。 smoothdamp,我的理解是平滑缓...

Less(初步了解) flex(弹性盒,伸缩盒) 像素 视口(viewport) 手机像素 完美视口

Less(初步了解) 1.less是一门css的预处理语言。 2.less是一个css的增强版,通过less可以编写更少的代码实现更强大的样式。 3.在less中添加了许多的新特性,像对变量的支持、对mixin的支持。 4.less的语法大体上和css语法一致,但是less中增添了许多对css的扩展,所以浏览器无法直接执行less代码,要执行必须向将les...

移动端H5页面遇到的问题总结

 最近刚做完一个移动端的项目,产品之无敌,过程之艰辛,我就不多说了,记录下在这个项目中遇到的问题,以防万一,虽然这些可能都是已经被N多前辈解决掉了的问题,也放在这里,算是为自己漫漫前端路铺了一颗小石子儿吧,也在文末留下自己未能解决的疑问,希望看到的朋友能解惑。   都知道做移动端的开发,在电脑上调试好了的东西,放在手机里可能真的秒秒钟就炸了,我发誓绝对没...

Unity表面着色器

表面着色器和之前无光照着色器不同,其中没有顶点着色器和片元着色器,而增加了光照函数; 接下写了一个求两个贴图的光照效果 两个贴图做插值运算: Shader "Custom/SurfaceShader" { Properties { _Color ("Color", Color) = (1,1,1,1)...

Unity多语言本地化改进版

简介 之前捣鼓过一个通过csv配置游戏多语言支持的小工具,但是发现使用过程中,通过notepad++去进行转码很不方便,并且直接将配置的csv不加密的放在游戏中心里感觉不是很踏实 于是乎~~ 新的方案 1.在PC/MAC平台上解析多语言配置,也就是editor运行环境中解析csv或者excel 2.通过在Editor运行过程中生成多个语言对象,然后序列化并...