找回密码
 立即注册
查看: 535|回复: 0

[UGUI图文混排一]TextMehPro(TMP)使用手册

[复制链接]
发表于 2022-1-17 16:18 | 显示全部楼层 |阅读模式
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界面,它基于CanvasRendererCanvasSystem进行工作,创建方式是“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“,接下来就会生成一个字体文件。


在场景中创建一个TMPText,并将其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="happy">    //默认图集+图名

<sprite="MySprites" index=9>      //指定图集+索引
<sprite="MySprites" name="happy"> //指定图集+图名
如果使用的时候觉得图片对其方式不行,可以选中图集,并且在它的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。


其中TextMeshProUGUI的Awake、OnEnable和.ctor都产生了大量的GC。查看代码可知,主要原因是由于 TMP_TextTMP_TextInfo 在创建时预创建了较多的数组变量,导致申请了较大的堆内存,另一方面,数组变量在中间使用的过程,存在Resize操作,又会产生新的堆内存申请。
比如当文本字数增加需要扩容时,会调用Resize函数,这会导致大量堆内存产生。
private void ResizeInternalArray<T>(ref T[] array)
{
     int newSize = Mathf.NextPowerOfTwo(array.Length + 1);
     Array.Resize(ref array, newSize);
}
为了优化这个问题,大家可以根据实际需要,将原先申请的内存缓存起来,在下一次创建的时候重新拿起来使用,避免每次都重新申请。
参考

[1] TextMesh Pro Documentation
[2] 知乎专栏:TMP的优势与缺点
[4] Unity中的SDF

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-12-23 12:30 , Processed in 0.101018 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表