[UGUI图文混排一]TextMehPro(TMP)使用手册
TextMeshPro (TMP)简介TMP是Unity的字体插件,它功能强大,使用简单,并且能够很好的支持图文混排。看一张效果图
Unity实现图文混排本来是一件很复杂的事情,但使用TMP,这将变得非常简单!
作为Unity 的最终文本解决方案,除了支持图文混排,它还支持矢量字体,可以很好的替代旧版的UGUI Text组件。在Unity2021中,Text组件已经被归纳到Legacy里面中。
现有的字体实现方式大致分为两种,一种是点阵字,另一种是矢量字。
[*]点阵字(Dot-matrix-fonts):点阵字也叫位图字(Bitmap-fonts),它将每个字都以一组二维像素信息表示,可以把这种字体的字理解为一张小图片,每一个字都是小图片。图片只要在放大或者缩小的时候就会产生马赛克或者锯齿,同理像素字因放大或缩小导致的失真会让阅读体验非常差。
[*]矢量字(Vector font):矢量字体保存的不是字体的图片,而是字体的笔画和走势。它使用贝塞尔(Bézier)曲线,绘制指令和数学公式来描述每个字形,这使得字符的轮廓可以缩放到任何大小依然保持高清晰度。
TMP中的文字渲染是基于SDF(Signed Distance Field)实现的,我们通过效果图比对一下,上面的是TMP中的文本,下面的是UGUI的普通文本,很明显当图片放大时下面的字体变得模糊了。
TMP这么优秀,我们看看怎么使用。
导入资源包
TextMesh Pro(TMP)是Unity中的内置资源,使用的时候只需要将导入即可,步骤为 "Window -> TextMeshPro -> Import TMP Essential Resources",导入完成后会创建一个名为"TextMehs Pro"的文件夹,这里面包含所需要的资源。如果想要看一些使用案例,也可以使用 "Window -> TextMeshPro -> Import TMP Examples & Extras" 导入实例场景。
组件概述
图文混排用到的主要是Text组件,TMP中Text分为两种
[*]用于3D世界,它基于MeshRenderer进行渲染,创建方式为“ GameObject -> 3D Object -> Text - TextMeshPro ”
[*]用于UI界面,它基于CanvasRenderer和CanvasSystem进行工作,创建方式是“GameObject -> UI -> Text - TextMeshPro”
光从属性界面上来看,它们两者之间并没有多大的差别,只是使用的场景不同。下图是UI Text的属性界面
可以看到,它被划分为三个区域,分别是文本输入区(Text Input)、主要设置区(Main Settings)以及额外设置区(Extra Settings)。
文本输入区(Text Input)
用来输入需要展示的文本,同时它也支持富文本标签(Rich Text Tag),定义的标签需要包含在 “<” 和 “>” 字符中间,类似于HTML。例如
<b>Bold</b> //粗体
<color=yellow>Yellow text</color>//设置字体颜色
<u>Underline</u> //下划线由于它支持的富文本标签非常多,这里就不列举了,想要了解的可以看文档 Rich Text。
主要设置区(Main Settings)
Main Settings主要是对文本格式进行设置,比如设置字体大小、颜色、布局等。
额外设置区(Extra Settings)
额外设置区是进一步控制文本外观和行为的附加选项。
材质编辑器和着色器(Material Editor & Shaders)
TextMeshPro包括几个自定义着色器和一个自定义材质编辑器,通过这些暴露出来的属性可以对材质进行修改。
着色器(Shader)
TMP有两组着色器,包括位图着色器(Bitmap shader)和一些有向距离场(SDF,Signed Distance Field)着色器,默认使用的是SDF着色器,这些Shader保存在TextMeshPro -> Shaders文件夹中。
所有的Shader都分为移动端和PC端两个版本,移动端消耗较低,但支持的效果相对也比较少。
材质编辑器功能面板(Material Editor Feature Panels)
在本节中,您将看到自定义材质编辑器使用的每个特性面板的概述。点击这些面板将隐藏或显示它们的功能,其中一些面板需要启用,以开启其功能,不同的面板可以在不同的着色器上使用。
Face面板
[*]Color:设置字符的颜色和透明度,这个颜色会和顶点颜色相结合。
[*]Texture:选择一张被应用于字符表面的贴图,Color属性会影响纹理的颜色。 纹理如何应用到字符/文本对象是由编辑面板中的映射选项(mapping options)控制的。 纹理选项不是在所有的着色器上可用
[*]Softness:控制字符表面的柔和度,
[*]Dilate:控制字符的粗细
[*]Gloss:当使用表面着色器时,控制字符表面的光泽度
Outline面板
[*]Color:控制字符轮廓的颜色和透明度。
[*]Thickness:控制字符轮廓的厚度。
Underlay面板
Underlay可用于为文本对象添加阴影或边框,这是增加小文本的对比度非常有效的方法。
[*]Color:设置Underlay的颜色和透明度
[*]Offset(X, Y):设置Underlay的位置,向X、Y方向的偏移
[*]Dilate:控制Underlay的粗细
[*]Softness:控制Underlay的柔和度
实战
我们看看怎么为TMP添加字体和表情包。
添加字体
刚导入的TextMesh Pro是不包含汉字的,这是我们可以导入字体资源,使用Windows的同学可以在”C:\Windows\Fonts“找到字体并导入。导入完成后,在Unity中选中刚才的字体,”右键 -> Create -> TextMeshPro -> Font Asset“,接下来就会生成一个字体文件。
在场景中创建一个TMP的Text,并将其Font Asset选中为刚才生成的资源,这是就可以正常现实中文了。
自定义表情包
在TMP中,通过富文本标签我们可以轻松的使用表情包。制作表情包首先要有一张图片,将其Texture Type设置为Sprite(2D and UI),Sprite Mode设置为Multiple。
选中图片,右键 Create -> TextMeshPro -> Sprite Asset
然后将创建好的文件放入 TextMesh Pro -> Resources -> Sprite Assets 文件夹下。
接下来修改默认的Sprite Asset文件,TextMesh Pro -> Resources 文件夹下找到TMP Settings设置文件,将它的默认Sprite Assets修改为新创建的。
调用Sprite图片有两种方式,分别是通过索引或名称
<sprite=9> //默认图集+索引
<sprite name=&#34;happy&#34;> //默认图集+图名
<sprite=&#34;MySprites&#34; index=9> //指定图集+索引
<sprite=&#34;MySprites&#34; name=&#34;happy&#34;> //指定图集+图名
如果使用的时候觉得图片对其方式不行,可以选中图集,并且在它的Sprite Glyph Tables中修改Global Offset & Scale属性。
缺点
TMP这么强大,理所当然也有会有不足之处。目前为止,遇到两个最明显的问题,第一个是DrawCall,第二是堆内存。
DrawCall
创建两个Text(TMP),可以看到它们只占用了一个DrawCall。这时我们修改其中一个的Outline属性,发现两个Text(TMP)一起发生了变化,这是因为它们使用了同一个Material。
也就是说,当我们为Text(TMP)添加特效时,如果只希望一个Text(TMP)使用该特效,则需要为它单独创建一个Material。为此我们可以先调整好需要的特效,然后点击Create Material Preset 创建一个新的Material。
这样两个就可以为两个Text设置不同的效果。
但由于两个Text(TMP)使用了不同的Material,因此产生了两个DrawCall。在实际开发中,可能会用到很多种不同效果的字体,这将会导致需要创建大量材质球,增加DrawCall。
堆内存
创建100个Text(TMP),可以看到它的GC Alloc达到了1.8MB。不使用TMP,那创建100个Text呢?经过测试,产生的GC是1.0MB。
http://pic4.zhimg.com/v2-827b019e6d2c521a692404705d9423db_r.jg
其中TextMeshProUGUI的Awake、OnEnable和.ctor都产生了大量的GC。查看代码可知,主要原因是由于 TMP_Text 和 TMP_TextInfo 在创建时预创建了较多的数组变量,导致申请了较大的堆内存,另一方面,数组变量在中间使用的过程,存在Resize操作,又会产生新的堆内存申请。
比如当文本字数增加需要扩容时,会调用Resize函数,这会导致大量堆内存产生。
private void ResizeInternalArray<T>(ref T[] array)
{
int newSize = Mathf.NextPowerOfTwo(array.Length + 1);
Array.Resize(ref array, newSize);
}
为了优化这个问题,大家可以根据实际需要,将原先申请的内存缓存起来,在下一次创建的时候重新拿起来使用,避免每次都重新申请。
参考
TextMesh Pro Documentation
知乎专栏:TMP的优势与缺点
Unity中的SDF
页:
[1]