感觉使用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“神码编程”也就在这基础上做了几处扩展和一些文章分享
2.2 代码实现思路
提前生成sprite区域信息,如果是一个系列的表情则根据sprite名字进行区分,当然后面也根据名字进行保留和查找。如angry_0\angry_1\angry_2\angry_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
代码中在解析字符中基本每次都在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快速移动时这部分的时间占用就很夸张了。
优化思路:
- 尽量减少无效sprite进入列表,限制每个Text中sprite的最大数量
- 采用Array.Copy的形式替代逐个赋值
(5) 占位符乱码清除方式
原始版本可能时作者计算错误了,清除乱码的UV位置其实只需要向后4个即可,但是也原始版本是按4 * Length(标签长度)来计算,这项的CPU占用率特别高。
(6) 动态表情更新方式
原始版本时在SpriteUpdate中每隔固定时间更新表情的索引(如果有)并重新更新Sprite Mesh内容。会产生一个问题:每种类型表情动画图片的数目不一样,那就很难保证每个动态表情都很自然的播放。提高更新的间隔意味着有些表情像发飙一样
优化思路:
每类型的表情中单独存放其时间间隔以及已经运行的时间,在Update中根据各自的情况进行更新。
(7)图片位置更新方式
原始代码中是在Text :SetVerticesDirty()中进行ParseText的操作并依赖SpriteManager中LaterUpdate更新图片的Mesh数据,产生的问题:
- SetVerticesDirty 是Text 任何变化都会调用接口,意味着ParseText的操作在ListView滚动过程中一直在进行。
- SpriteManager中LaterUpdate更新与Text位置变化不同步,滚动时很明显的可以看到sprite的位置偏移
优化思路:
- ParseText只在text文本内容变化时进行更新,可通过重载Text的text属性实现
- 在ListView滚动过程中 sprite变化的只有位置信息,所以只更新位置即可,并且直接更新MESH,不等待SpriteManager。
(8)其他
对应的还有编辑器、数据结构、贴图资源管理等的优化
3.2 新增功能
(1)支持简化标签
支持 "[xxxxx]"来替代冗长的设置
(2)图片层级管理
方便单个Canvas下多个层级,让Text 可以直接设置SpriteManager或者找最近的一个。
(3)增加文本与图片间隔设置
3.3 待优化内容
(1)下划线解析和超链接解析都是基于字符位置对应实际字符顶点位置
(2)字符串解析
(3)图片Mesh
(4)多张sprite Asset
3.4 优化效果
测试方式,屏幕中160个动画表情的情况,在ListView中快速滚动下进行测试的性能曲线(主要时CPU);
优化前
优化后
原生Text, 有占位符,无表情
4 小结
采用这种方案各种原因都有,有好处也有弊端,就像层级问题,解决起来会有点头痛。经过一段时间优化勉强可以在移动端满足需求,不过还有很多可以继续优化的空间。
GITHUB工程文件:https://github.com/carlosCn/Unity-EmojiText
百度网盘资源:http://pan.baidu.com/s/1geZuVNd
欢迎继续补充。
Unity琐碎(3) UGUI 图文混排解决方案和优化的更多相关文章
-
Unity UGUI图文混排源码(二)
Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304 Unity UGUI图文混排源码(二):ht ...
-
Unity UGUI图文混排源码(一)
Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304 Unity UGUI图文混排源码(二):ht ...
-
Unity UGUI图文混排源码(三) -- 动态表情
这里是根据图文混排源码(二)进一步修改的,其他链接也不贴了,就贴一个链接就好了,第一次看这文章的同学可以先去看看其他几篇文章 Unity UGUI图文混排源码(二):http://blog.csdn. ...
-
Unity UGUI图文混排(七) -- 下划线
之前更新超链接的时候,忘了搭配实现一个下划线的功能,这篇文章就是来补上这一个功能,时间有点长,一方面没有很好的思路,一方面也没多少时间. 先在网上收集了一下下划线的实现操作,一种是在文本下再创建一个文 ...
-
Unity UGUI图文混排(六) -- 超链接
图文混排更新到超链接这儿,好像也差不多了,不过就在最后一点,博主也表现得相当不专业,直接整合了山中双木林同学提供的超链接的解决方案,博主甚至没来得及细看就直接复制了,但感觉还是挺好用的. 博主已经将超 ...
-
[UGUI]图文混排(二):Text源码分析
UGUI源码: https://bitbucket.org/Unity-Technologies/ui/downloads/?tab=tags 首先下载一份UGUI源码,这里我下载的版本是5.3.2f ...
-
[UGUI]图文混排(三):资源管理
1.图文混排中的资源,主要是图片. 2.所谓的资源管理,可以分为资源对象池和资源加载这两部分.这里是为图文混排单独做一套资源管理,当然也可以改为调用项目中的资源管理. RichTextResource ...
-
[UGUI]图文混排(一):标签制定和解析
参考链接: https://github.com/SylarLi/RichText/tree/master/Assets/Scripts 正则表达式: https://blog.csdn.net/ly ...
-
Unity UGUI图文混排(五) -- 一张图集对应多个Text
继上一篇说的更新了一张图集对应多个Text的功能,为了节省资源嘛 这里,但是也没有舍弃之前的一个Text一个图集,因为我感觉应该两个都有用,于是我重新写了一个脚本 1.其实大体跟前面的都没变,解析标签 ...
随机推荐
-
ASP.NET MVC 路由(一)
ASP.NET MVC路由(一) 前言 从这一章开始,我们即将进入MVC的世界,在学习MVC的过程中在网上搜索了一下,资料还是蛮多的,只不过对于我这样的初学者来看还是有点难度,自己就想看到有一篇引导性 ...
-
Visual Studio Productivity Power Tools居然也开源
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:微软开源玩得嗨,连VS扩展插件Productivity Power Tools也开源. ...
-
HTML5自学笔记[ 2 ]新增表单控件和表单属性
新增<input>属性type="email",自动验证,若输入不为邮箱,则不能提交. 新增<input>属性type="tel",在移 ...
-
java多线程编程核心技术——第二章
第一节synchronized同步方法目录 1.1方法内的变量为线程安全的 1.2实例变量非线程安全 1.3多个对象多个锁 1.4synchronized方法与锁对象 1.5脏读 1.6synchro ...
-
project3 blockchain
[概念] 做服务的时候main里面不能单独有东西,都得包起来. Identifier expected是因为没有main函数 雾草,task3还要加proxy, add再干别的.难受!妈的,什么代理模 ...
-
什么是CSS hack?
1.什么是CSS hack? CSS hack是通过在CSS样式中加入一些特殊的符号,让不同的浏览器识别不同的符号(什么样的浏览器识别什么样的符号是有标准的,CSS hack就是让你记住这个标准),以 ...
-
Elasticsearch 5.X 使用 Docker 运行使用 Head 插件
ES 5.X 版本后就不支持 elasticsearch-head 以插件方式来安装了. for Elasticsearch 5.x: site plugins are not supported. ...
-
ES6之Promise用法详解
一 前言 本文主要对ES6的Promise进行一些入门级的介绍.要想学习一个知识点,肯定是从三个方面出发,what.why.how.下面就跟着我一步步学习吧~ 二 什么是Promise 首先是what ...
-
[React] Write a stateful Component with the React useState Hook and TypeScript
Here we refactor a React TypeScript class component to a function component with a useState hook and ...
-
微软BI 之SSAS 系列 - 在SQL Server 2012 中开发 Analysis Services Multidimensional Project
SQL Server 2012 中提供了开发 SSAS 项目的两种模型,一种是新增加的 Tabular Model 表格模型,另一种就是原始的 Multidimensional Model 多维模型. ...