![9c0909eef240bd29898331c34b8f5218.png](https://i-blog.csdnimg.cn/blog_migrate/8813b8291ed654e0725af47f0456402a.jpeg)
简介
打字机模式是游戏开发中十分常用的功能,其效果就是对文本进行逐文字的显示,目的是增加玩家的阅读感。下图展示了打字机效果:
![c95020a3d1a63eb583b5c485f0344843.gif](https://i-blog.csdnimg.cn/blog_migrate/2113e57ed9c4c7751d1fd5eec064fadd.gif)
调研
基于文本实现打字机的效果其实并不复杂,理想状态下,我们只需要将文本中的文字逐个拿出来进行显示就可以了。但生活往往会给你个小惊喜,那就是:富文本。富文本通过标签来实现常见的文字颜色、加粗、斜体等功能,它是嵌入在文本内容中的。以上图的示例来说,包含了富文本的完整文本内容为:
你好,我是<color=#ff0000ff><i><b>打</b>字</i><b>机</b></color>。我是<b>粗体</b>。我是<i>斜体</i>。
如果我们不对富文本做处理,而仅仅将文本中的文字逐个拿出来进行显示,那么效果就是错误的,如图:
![bc8cfec52e332c0bbbcb169107989e50.gif](https://i-blog.csdnimg.cn/blog_migrate/297c17be67c26933bbeadc2a575eabac.gif)
目前网络上比较多的实现都是没有考虑过富文本的版本,也就是我前文提到的逐个显示文本中文字的方法,这个方法没有正确的处理富文本。因此本文的目标就是探讨一种可以正确处理富文本的打字机模式的实现,由于涉及到公司的内容权限,因此代码方面不会有太多,主要还是想和大家分享一下心得体会。
Unity定义的富文本
Unity定义了丰富的富文本来增强文字的显示效果,具体可以参考官方文档:
Unity - Manual: Rich Textdocs.unity3d.com![d0ea97103d4195ac0db1acb964ecb290.png](https://i-blog.csdnimg.cn/blog_migrate/735b643a1ad7a4b3f3fbc3dff7ebfeda.png)
这里我们不是为了介绍富文本包含哪些可用内容,而是想说一说Unity对富文本的解析机制给我们实现打字机模式所带来的不便。
第一个是Unity只有在看到富文本标签匹配对时才会将文字显示为富文本指定的格式。举例来说,如果我们有富文本"<color=#ff0000ff>我是红色",那么在Unity中会显示为:
![3ab601e7d1a4486911ffe57b65402100.png](https://i-blog.csdnimg.cn/blog_migrate/6c482968c72b91c81fe9fd7ef867d8c7.png)
因为此时Unity并没有看到富文本标签的结束标记,只有在富文本标签匹配时,Unity才会显示正确的格式。如"<color=#ff0000ff>我是红色</color>":
![5aa2e672044a7d1ac1a4229b56bfd14f.png](https://i-blog.csdnimg.cn/blog_migrate/d0b6d0eaef6ab324bc3cfbbc075adbae.png)
因此在打字机模式下就无法做到当遇到富文本标签的起始标记时,直接将起始标记完整的填入目标显示文本,然后接着逐字的显示之后的文本。
第二个是Unity在解析富文本标签时,是不允许互相穿插的,例如文本"<b>无法<i>穿插</b></i>"就无法被正确处理,显示效果如图:
![69801f7a0353b71bef64b578ced646f3.png](https://i-blog.csdnimg.cn/blog_migrate/4dbdfda1470003704db1464cd7e13fa7.png)
这个问题在我想通过控制文字显隐的方式来实现打字机效果时给了我当头一棒,之后会有提及。
最初的想法:控制文字显隐!
经过和同事的简单商讨,我们最初的想法是希望以控制文字显隐的方式来实现打字机效果。我们在想是不是可以通过更改传递给Unity的顶点数据和索引数据来控制显示哪些文字,因为Unity是以网格的方式渲染文字的,如图:
![0d392841755d24313297072b369e1dc2.png](https://i-blog.csdnimg.cn/blog_migrate/da194f98a7688997b3dcbd6c9837c677.jpeg)
虽然之后我没有尝试实现这种方案,但控制文字显隐的思路给了我启发,我想到是不是可以通过添加富文本标签"<color=#00000000></color>"来达到隐藏文字的效果。举个例子,如果当前我们的文本是"隐藏我呀",当要显示第一个文字时,我们可以将文本改变为"隐<color=#00000000>藏我呀</color>",最终的效果就是只有第一个文字会被显示,如图:
![0b0655a33f782b50970388bbc0e5dec1.png](https://i-blog.csdnimg.cn/blog_migrate/bcdeba52c2cb8d21c89537cc42cc073b.png)
在逐个显示文字的过程中,只需要在相应的位置插入起始标记"<color=#00000000>",并且在文本的最后插入结束标记"</color>"就可以隐藏相应的文本了。
这个方案一开始我也觉得没毛病,直到遇到了前文提到的穿插问题,我们无法在一个富文本标签对之间插入"<color=#00000000>",并且在文本最后插入"</color>",Unity无法正确解析。
以富文本"我是<b>粗体</b>,我是<i>斜体</i>"为例,当显示到"粗"这个字时,最终生成的文本其实是"我是<b>粗<color=#00000000>体</b>,我是<i>斜体</i></color>",由于Unity无法正确解析穿插的富文本,因此最终的显示效果就是不正确的,如图:
![1dafbc95d9c6dff8bf603e67b5272a66.png](https://i-blog.csdnimg.cn/blog_migrate/83314e3ce6040fa8a84f3d36cedefea7.png)
要想正确的实现隐藏效果,就必须小心的处理富文本标签对,不能出现穿插的问题。以上面的例子来说,我们最终生成的文本必须是"我是<b>粗<color=#00000000>体</color></b><color=#00000000>,我是<i>斜体</i></color>",也就是必须保证富文本标签不能穿插。
除去这个问题之外,隐藏的方式还有一个问题,就是虽然看不到文字,但其实文字还是真实存在在那儿的,所以文本的对齐方式会有问题。举个例子,假设我们期望文本的对齐方式是居中的,那么当显示到第一个文字时,我们自然而然的希望这个文字出现在中间,但由于所有文字其实都已经生成,只不过其他文字是被隐藏了,因此第一个文字就不会出现在中间,错误的效果如图:
![2b414851763dc552cb6c7e85c0196d08.gif](https://i-blog.csdnimg.cn/blog_migrate/edb92c12ae97e92ef91883ff59bd7ef8.gif)
正确的效果应该如下图:
![bd186304a6772dd1c43efef30e9049ed.gif](https://i-blog.csdnimg.cn/blog_migrate/99882833996cf10a46735c8a08f82bde.gif)
由于上文提到的两个问题,控制文字显隐的方式没有办法达到我们的需求。
实现方案:匹配富文本标签
既然Unity在解析富文本格式时既需要看到完整的富文本标签又不允许穿插,那我们就满足它的要求,手动来匹配富文本标签。
有了这样的思路之后,就可以动手实现啦,整体的方案并不算太复杂,流程如下图所示:
![1c126d2a5b8c82ecf0b794aa71d9bdf8.png](https://i-blog.csdnimg.cn/blog_migrate/9229a6c08b588eb29ff1d7e2d2b046e6.jpeg)
这里的关键点在于每次对当前文字进行解析时,首先判断它是不是一个富文本标签的起始标记,如果是的话,我们就将这个富文本标签压入一个栈中,之后,就跳过这个标记,继续解析下一个文字。如果不是的话,还需要判断当前文字是不是一个富文本标签的结束标记,如果是的话,就需要从富文本标签栈中弹出标签,这样的话,才能最终保证富文本标签是匹配的。在解析完当前要显示的文字之后,我们就需要查询富文本标签栈,看其中是否有富文本标签。如果有的话,就证明在本次解析的过程中,我们遇到了富文本标签的起始标记,但没有遇到富文本标签的结束标记,此时就需要相应的补上富文本标签的结束标记,以此来达到匹配的目的。
下面以一个例子来进行说明,帮助大家更容易的理解:
![912b7fe8664820c43cf90a7be2f54721.png](https://i-blog.csdnimg.cn/blog_migrate/36f2846cb856be1bfff3814a5cfc90db.png)
开始时,我们解析的文字为"<",此时经过判断发现它是富文本标签的起始标记,则将该标签压入富文本标签栈中,同时将该标签添加到显示字符串。接着我们解析文字"你",它是一个正常显示的文字,将它添加到显示字符串。在解析文字的过程结束后,我们去查询富文本标签栈,发现其中有一个color标签,则需要在显示字符串中添加color标签的结束标记。最终的显示字符串就是"<color=red>你</color>"。
![e383b55bf76a494338029c0d210044a6.png](https://i-blog.csdnimg.cn/blog_migrate/87ac35118f47c7aa196416d777905f78.png)
在下一步的处理中,我们继续解析文字"<",此时经过判断发现它是富文本标签的起始标记,则将该标签压入富文本标签栈中,同时将该标签添加到显示字符串。接着我们解析文字"好",它是一个正常显示的文字,将它添加到显示字符串。在解析文字的过程结束后,我们去查询富文本标签栈,发现其中有一个color标签,还有一个b标签,此时我们需要按倒序的方式添加结束标记以保证富文本标签的匹配。最终的显示字符串就是"<color=red>你<b>好</b></color>"。
![d46e5246c4301403ef0613f67e1559c6.png](https://i-blog.csdnimg.cn/blog_migrate/cb43fbe9fd2dc55faccac4a3b754f768.png)
最后,我们解析文字"<",发现它是一个富文本标签的结束标记,此时就需要从富文本栈中弹出标签,表明一个富文本标签匹配对的结束,同时将这个结束标记添加到显示字符串中。接着继续解析文字"<",发现它仍旧是一个富文本标签的结束标记,则处理流程和之前一样,从富文本栈中弹出标签,同时将这个结束标记添加到显示字符串中。到这里,全部的文本内容都已经处理完毕了,打字机模式结束。
总结与展望
本文阐述了基于Unity的UIText实现的打字机效果,更多想分享给大家的是我自己的想法和思路。在真正实现的过程中还会遇到许多细节上的问题,并且对于字符串操作所带来的内存分配的优化也是进一步需要考虑的问题。
本人能力有限,如有错误和更好的想法,望不吝赐教。知识只有分享了才能体现它的作用和价值,共勉 :)。
参考资料:
- https://docs.unity3d.com/Manual/StyledText.html
本文固定链接: EnigmaJJ:基于Unity的UIText实现打字机效果
转载请注明: EnigmaJJ 2019年09月22日 于 知乎 发表