以TextMeshPro为基础的文本效果拓展
之前网易初赛独立游戏的时候完成的功能,现在有时间便完善并写个文章记录一下。
需求分析
当时游戏策划是以旁白为主要载体,所以与玩家的对话文本需要更多的表现,如蔚蓝中里面文本的效果:
可以看到有两个部分需要拓展,一个是文本打印方式,另一个部分是文字文本效果,这两个都可以通过改变文字mesh的顶点来实现。
核心功能实现
最主要的功能就是对文本顶点的操作。
1. 获取顶点信息
在TextMeshPro中每个字符的顶点是可以直接获取的,不过在获取顶点信息的时候需要调用TMP_Text.ForceMeshUpdate(Boolean, Boolean)函数来强制mesh的更新,不然在获取Tmp_Text.textInfo为空或者Tmp_Text.textInfo中信息有误。
2. 修改顶点信息
在修改顶点信息之前我们需要知道如何TextMeshPro是如何生成mesh的。
在TextMeshPro中每个文字的顶点个数是四个,顶点的下标按照下图表示:
这里为了方便理解,举个遍历所有字符操作:
//从第一个字符开始遍历到最后一个字符
for (int i = 0; i < textInfo.characterCount; i++)
{
//单个字符Info
TMP_CharacterInfo charInfo =textInfo.characterInfo[i];
//获取字符的material下标
int materialIndex =textInfo.characterInfo[i].materialReferenceIndex;
//获取该字符的mesh信息(TMP_MeshInfo)
var meshInfo=textInfo.meshInfo[materialIndex];
//获取该字符的顶点信息
var verts=meshInfo.vertices;
//获取该字符的颜色信息
var colors=meshInfo.colors32;
}
这里通过修改上面的函数就可以实现我们想要的效果,具体对顶点的操作可以去查看项目文件。
官方案例中在每一次对顶点操作前都会调用TMP_Text.ForceMeshUpdate(Boolean, Boolean)函数,可以理解为在操作前,先把文本按照正常的方式生成,获得的textInfo也就是没有任何操作时文本的信息,对于文字文本效果这是很好的,因为大多数文字文本效果都是不需要之前操作的信息。
但是对于打印效果就会有更多的消耗,例如要实现一秒显示一个字符,要么在每次提交前都要计算所有顶点的颜色,计算后提交,要么自己记录颜色信息,修改要改变的颜色,提交前都将textInfo的颜色信息替换成记录的颜色信息,再提交,笔者采取了后者。
这里如果避免每帧调用TMP_Text.ForceMeshUpdate(Boolean, Boolean)函数,而是调用一次该函数保存textInfo的信息,在修改时根据保存的信息修改,也许能优化一下,但是官方案例中并没有这种操作示例,笔者也不确定这样做会不会影响TextMeshPro的其他功能,所以没有做更多的尝试,采取与官方一样的方式。
3. 提交顶点信息
当你将textInfo中的顶点信息修改完毕后,调用TMP_Text.UpdateVertexData(TMP_VertexDataUpdateFlags),参数是内置的枚举,决定选择提交什么部分。
其他设计
1. 文本效果继承ScriptObject
文本效果继承ScriptObject主要考虑两点
- 一个文本效果可能有多个参数不同的对象,使用ScriptObject就可以直接多个创建,并且长期在项目文件中。
- 序列化,这会有很多便利,比如修改参数,增删效果等等可以直接在编辑器中操作。
下面是两种文本效果的类图:
当需要拓展文本效果时,只需要继承想要拓展的基类,覆写父方法即可,具体可查看笔者写的几个效果代码。
2. 富文本匹配
为了帮助策划使用,这里采用了富文本的形式来设置在某个段落实现某种效果,如需要对123456789字符串,1234需要浮动效果,789需要抖动效果,就可以将<float>1234</float>56<shake>789<shake>传入TMP_ExpandEffectContainer,自定义富文本标签名即是TMP_BaseRichText的richTextName属性。
实现算法就是通过栈进行匹配,具体算法查看TMP_ExpandEffectContainer类的GetRichTextIndex(string s)方法,由于该算法是需要富文本的信息需要按照富文本名字从小到大排序,所以需要对TMP_ExpandEffectContainer的Editor的拓展,拓展使用了ReorderableList,对富文本效果数组进行了重新绘制,并增加了列表修改时回调排序函数,具体Editor拓展代码查看TMP_ExpandEffectContainerEditor类。
3. 状态机
为了逻辑的清晰所以设计了类内置状态机。
根据需求可知包含三个状态Sleeping(未启用),Typing(打印中),Completed(打印完毕),具体的代码可以查看项目文件中的脚本文件内TMP_ExpandEffectContainer/TMP_ContainerFSM中的代码。
后记
这个拓展是以TextMeshPro修改文字Mesh顶点实现的,UGUI也是可以的,可以参考《Unity UGUI通过修改顶点实现文字打字机》这篇文章。
拓展中还是有蛮多地方可以优化的,但是耗在这里的时间太多了,所以就在此停笔了,之后可能不定期地更新更多的效果或者优化,具体可查看Github页面。
参考
链接
项目Github链接,如果有帮助麻烦点个小星星(点赞