TextMeshPro

文档

[官方文档](TextMesh Pro Documentation | TextMeshPro | 4.0.0-pre.2 (unity3d.com))

Unity 中的 TextMesh Pro 简介 |科德科 — Introduction to TextMesh Pro in Unity | Kodeco

[UGUI图文混排一]TextMehPro(TMP)使用手册 - 知乎 (zhihu.com)

动态/静态字体区别

动态字体资源 |文本网格专业版 |4.0.0-pre.2 — Dynamic fonts assets | TextMeshPro | 4.0.0-pre.2 (unity3d.com)

TextMeshPro现在又自动扩容机制,即指定字体自动使用字体中的文字,不用手动生成,代价是稍许消耗.

动态字体需要保留原来的字体文件(.ttf.otf),不然无法动态生成,这意味者会增大包体,而静态字体可以生成之后删除

制作中文字用得到的常用字

wy-luke/Unity-TextMeshPro-Chinese-Characters-Set

标签

Unity手游实战:从0开始SLG——TextMeshPro(三)Rich Text - 知乎 (zhihu.com)

[UGUI图文混排二]TMP支持的富文本(Rich Text)标签 - 知乎 (zhihu.com)

可以查看这个类 TMP_RichTextTagsCommon.cs

  • 给图文中的图加颜色 <sprite index=0 color=#FFEB04>

  • 缩进 <line-indent=15%>

  • 图文帧动画的图帧动画,只支持2帧<anim="first frame, last frame, frame rate"> eg: <sprite index=0 anim="1,2,1"> 注:rate 越小越慢,最小为1

换行

image-20240503212053605

这里说明一下LineBreaking Following CharactersLineBreaking Leading Characters

LineBreaking Following Characters: 里面定义的标点字符,表示跟随即不能出现在句首,例如句号逗号。。。

LineBreaking Leading Characters:定义的标点符号,表示可以启新行,能出现在句首,例如括号。。。

食用方案

  1. 使用7000字+常用符号作为常用字,把(unicode:\u25A1)打入进去,当文字找不到时候自动会使用□占位(前提是你的字体本身就含有该字),然后使用工具将字体贴图压缩,作为静态字体为默认字体

  2. 找一个全字体设置为动态字体作为最后的fallback,要使用反向裁剪去除该字体中7000字+常用符号,只保留其他文字,(当然也要包含制作7000字+常用符号时miss的文字)目的是精简该字体,减少内存

当然第二步如果不想做动态字体,也可以弄个只有生僻字的静态字体(可以参考通用规范汉子表大概8160多字 - 常用7000字)或者当运行时缺什么字就把这个字打到fallback子体中

在TMP中当文字缺失会出现如下警告

The character with Unicode value xxxx was not found in the [xxxx] font asset or any potential fallbacks. It was replaced by Unicode character xxxx in text object [xxxx].

这个警告在TMPro_UGUI_Private.cs脚本中,如果有必要可以修改源代码变成Error

还有这个反向裁剪该怎么实现,我参考ai给的py脚本使用fonttools库,但是还没验证,不知道有没有道友有明确的验证的方案.

7000字+常用符号: https://github.com/wy-luke/Unity-TextMeshPro-Chinese-Characters-Set

字体贴图压缩: https://www.lfzxb.top/unity-textmeshpro-something/

TextMeshPro打包AssetBundle

当导入TestMeshPro的时候会自动导入一些资源包,大部分都在Resources文件夹下

image-20240503221431727

image-20240503211840790

操作步骤

2025年8月12日更新

现在可以不修改tmp源代码直接利用反射设置

Runtime
1
2
3
4
5
6
7
8
9
10
11
12
13
//在合适地方加载
string tmpsettingPath = "fonts/TMP Settings.asset";
var tmpSetting = GameEngine.AssetModule.LoadAsset<TMP_Settings>(tmpsettingPath);
#if UNITY_EDITOR
if (tmpSetting == null)
{
ZLog.LogError($"未找到TMP Settings.asset path:{tmpsettingPath},请创建并放在: {tmpsettingPath}");
return;
}
#endif
// 反射替换静态字段 s_Instance
var field = typeof(TMP_Settings).GetField("s_Instance", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, tmpSetting);
Editor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[InitializeOnLoad]
public static class TextMeshProSetting
{
static TextMeshProSetting()
{
var guids = AssetDatabase.FindAssets("t:ScriptableObject TMP Settings");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var obj = AssetDatabase.LoadMainAssetAtPath(path);
if (obj is TMP_Settings _settings)
{
var field = typeof(TMP_Settings).GetField("s_Instance", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, _settings);
break;
}
}
}
}

  1. 将TextMeshPro包变成本地包

image-20240503220912444

  1. 修改源代码

    主要是修改TMP_Settings中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/// <summary>
/// Get a singleton instance of the settings class.
/// </summary>
public static TMP_Settings instance
{
get
{
if (TMP_Settings.s_Instance == null)
{
//从这里可以看到是从Resources加载
TMP_Settings.s_Instance = Resources.Load<TMP_Settings>("TMP Settings");

//在这里会加载TMP_Setting,如果没有就导入资源包
#if UNITY_EDITOR
// Make sure TextMesh Pro UPM packages resources have been added to the user project
if (TMP_Settings.s_Instance == null)
{
// Open TMP Resources Importer
TMP_PackageResourceImporterWindow.ShowPackageImporterWindow();
}
#endif
}

return TMP_Settings.s_Instance;
}
}

修改为(不建议 建议使用更新方案)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
        public static TMP_Settings instance
{
get
{
if (TMP_Settings.s_Instance == null)
{
//TMP_Settings.s_Instance = Resources.Load<TMP_Settings>("TMP Settings");
#if UNITY_EDITOR
//使用编辑器加载
TMP_Settings.s_Instance = AssetDatabase.LoadAssetAtPath<TMP_Settings>("Assets/Assetbundle/fonts/TMP Settings.asset");
// Make sure TextMesh Pro UPM packages resources have been added to the user project
if (TMP_Settings.s_Instance == null)
{
// Open TMP Resources Importer
TMP_PackageResourceImporterWindow.ShowPackageImporterWindow();
}
#endif
}

return TMP_Settings.s_Instance;
}
set
{
//runtime下 使用 set 赋值
TMP_Settings.s_Instance = value;
}
}

2025年7月9日 更新 才用AssetDatabase.FindAssets不使用固定路径 不要修改 TMP Settings名字(当然想改名字直接把查找变成AssetDatabase.FindAssets(“t:ScriptableObject”))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static TMP_Settings instance
{
get
{
if (TMP_Settings.s_Instance == null)
{
#if UNITY_EDITOR
var guids = AssetDatabase.FindAssets("t:ScriptableObject TMP Settings");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var obj = AssetDatabase.LoadMainAssetAtPath(path);
if (obj is TMP_Settings _settings)
{
TMP_Settings.s_Instance = _settings;
break;
}
}
// Make sure TextMesh Pro UPM packages resources have been added to the user project
if (TMP_Settings.s_Instance == null)
{
// Open TMP Resources Importer
TMP_PackageResourceImporterWindow.ShowPackageImporterWindow();
}
#endif
}
return TMP_Settings.s_Instance;
}
set
{
//runtime下 使用 set 赋值
TMP_Settings.s_Instance = value;
}
}

在启动运行时

1
TMP_Settings.instance = GameEngine.AssetModule.LoadAsset<TMP_Settings>("fonts/TMP Settings.asset");

修改Font Asset Creator 窗口Glyph Report存放位置

当使用Tmp的Font Asset Creator时,如果出现miss文字他会生成一个Glyph Report.txt文件,这个文件必须是Assets/TextMesh Pro/Glyph Report.txt路径,所以当你一旦更改了TextMesh Pro文件夹的名字或者位置都不会生成。

修改 TMPro_FontAssetCreatorWindow.cs->UpdateRenderFeedbackWindow()源代码
源代码

1
2
3
4
5
6
7
 // Save Missing Glyph Report file
if (Directory.Exists("Assets/TextMesh Pro"))
{
missingGlyphReport = System.Text.RegularExpressions.Regex.Replace(missingGlyphReport, @"<[^>]*>", string.Empty);
File.WriteAllText("Assets/TextMesh Pro/Glyph Report.txt", missingGlyphReport);
AssetDatabase.Refresh();
}

直接改成存放到Asset目录下,防止因为修改了文件夹而没有正确存放位置

1
2
3
4
5
6
//if (Directory.Exists("Assets/TextMesh Pro"))
// {
missingGlyphReport = System.Text.RegularExpressions.Regex.Replace(missingGlyphReport, @"<[^>]*>", string.Empty);
File.WriteAllText("Assets/Glyph Report.txt", missingGlyphReport);
AssetDatabase.Refresh();
//}

如何打入□和自定义Miss Character

如何打入□

首先得确保你的源文件字体有这个□字,验证方案也很简单,直接将字体生成一个Dynamic字体然后附加到一个textmeshpro组件上,然后在文本中打入查看是否显示,能显示出来说明就有,如果没有可以找个通用的字(比如思源)做fallback。

然后讲你的txt通用字中加入□这个字(截止目前2025年9月8日 [https://github.com/wy-luke/Unity-TextMeshPro-Chinese-Characters-Set] 这个库中已经该字 )

接着就是常规操作了

自定义Miss Character

Tmp源码文件TMPro_UGUI_Private.cs中有如下代码

1
unicode = unicodeChars[i].unicode = TMP_Settings.missingGlyphCharacter == 0 ? 9633 : TMP_Settings.missingGlyphCharacter;

Tmp默认是使用了一个(unicode:\u25A1,十进制是9633)来占位当miss的文字。能显示这个的前提是你的字体支持这个字,当字体本身就不支持这个字时,就会发现miss时不会被任何字占位。

当字体本身就不支持时,有两个解决办法

  • 添加一个支持的fallback字体
  • 自定义一个字体来占用

两种方案都很好实现,第二种方案就是在TMP SettingMissing Character Unicode添加一个想要的unicode(十进制)

image-20250908183432252

示例

1
2
3
4
5
6
7
8
public class NewMonoBehaviour : MonoBehaviour
{
public TextMeshProUGUI text;
private void Start()
{
text.text = "你好哒实际哒";
}
}

现在有字体A中没有这个字,直接运行显示

image-20250908183918496

随便找个字符来替换Missing Character

字符 名称 八进制 十进制 十六进制
U+262F 23057 9775 0x262F

将9775 填写在TMP SettingMissing Character Unicode,再运行

image-20250908184147947

当然前提是你的字体A能支持☯这个字,如果不支持可以支持的用个fallback(比如思源),就把这一个文字打进去