news 2026/10/13 18:18:43

Unity TextMeshPro中文字体字符集制作:开发期避免缺字与图集优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity TextMeshPro中文字体字符集制作:开发期避免缺字与图集优化

做Unity项目时,第一次给TextMeshPro接入中文字体的人,大概率都见过满屏的“口口口”。尤其文案为了测试花里胡哨输入几个生僻字,结果UI面板上整整齐齐一行方块,美术瞥一眼就开始怀疑人生。这个问题的根源很简单:TextMeshPro的字体资产不是“字体文件直接拖进去就能用”,它需要把字符逐个烘焙到图集上,而默认的字符集只包含ASCII或者英文字母数字,压根没有简体中文的份。所以这篇文章就围绕“Unity TextMeshPro制作字体用的简体中文字符集”这件事展开,把我自己开发期怎么准备字符集文件、怎么在Font Asset Creator里配置、怎么控制开发版和上线版的资源差异,完整讲一遍。

如果你只是在做原型、做开发迭代,不追求正式包体体积优化,那这套流程基本能让你一次弄完,后面UI上的中文再也不缺字。

1. 为什么TextMeshPro默认不认中文:从字符到字形图集的完整链路

1.2 一个字符的“旅行”路径

TextMeshPro渲染文本时,可不像传统UGUI的Text那样把整串字符直接交给底层字体渲染。它的工作方式是把字符串拆成一个个Unicode码点,然后在字体资产的字形映射表里查找对应的字形索引,找到之后从预烘焙好的图集Atlas上取一小块纹理贴图拼到Canvas上。这个过程听起来简单,但决定了一个核心事实:字体资产里必须提前包含这个字符,否则渲染管线就直接给个占位符“□”。

所以你看到满屏口口口,不是Unity的字体引擎坏了,而是你的字体资产字符表里根本没有“口口口”对应的汉字码点。TMP默认新建的字体资产,字符集通常只覆盖ASCII范围内那95个英文、数字和符号。汉字不在里面,自然没得渲染。

1.3 只看这几个字符集选项就够了

TMP的Font Asset Creator里,Character Set下拉框选项很多,但实际日常工作中翻来覆去就那几个:

字符集选项说明适用场景
ASCII32到126号字符,包含英文字母、数字、英文标点纯英文界面
ASCII Lowercase / Uppercase只包含小写或大写英文字母特殊UI控件
Custom Range手动输入Unicode十六进制区间需要临时补充少量字符
Unicode Range (Hex / Decimal)按Unicode码点范围批量选字符一次导入CJK统一表意文字区段
Characters from File从外部UTF-8文本文件导入字符列表简体中文字符集最常用方式

做简体中文项目,基本就是“Characters from File”和“Unicode Range (Hex)”这两种。Unicode Range如果直接输4E00-9FFF,会一次性拉进来将近两万多个CJK字符,开发期图集压力大,正式包也会明显撑大内存。所以我的做法是:字符集文件来控制到底收录哪些字,这样以后删减、统计都方便。

1.4 “仅用于开发”到底指什么

标题里说的“仅用于开发”,指的其实是资源策略,不是字符集文件本身有问题。开发阶段我们希望稳定、缺字率低,所以倾向于做一份覆盖几千个常用汉字的完整字符集,把图集一次性烘焙好,编辑器和真机跑起来都不会因为缺字报错。发布阶段则要控制内存和包体,通常切换到动态字体或者使用运行时生成的子集字体。这一点后面专门展开说,但你要先理解:字符集文件只是你喂给TMP的“菜单”,开发环境用大菜单,线上用精菜单,灵活切换才是重点。

2. 一份开发用简体中文字符集怎么来:三条高效途径

2.1 途径一:直接采用官方常用字表整理

国内最权威的《现代汉语常用字表》把汉字分成常用字2500个和次常用字1000个,合起来是3500个汉字。这个规模对开发期来说非常合理:覆盖绝大多数文案内容,字体图集大小可控,生成时间也快。你可以在网上找到现成的纯文本版本,复制到编辑器里,只留下汉字和必要标点即可。

注意一点:网络流传的常用字表文件质量参差不齐。有的文件里混了全角空格、隐藏控制符,有的标点字符缺失。拿到之后最好自己过一遍,确认第一行第一个字符是汉字而不是空格。如果一个文件开头就粘着BOM或者不可见字符,TMP照样会把它当成一个待生成的字符,虽然没大害,但图集里会莫名其妙多出一个没用的空字形。

2.2 途径二:用脚本按GB2312编码区间直接生成

如果你想要的不是3500字,而是GB2312全字库的6763个汉字,完全可以自己用脚本生成,不依赖网上的转载文本。简体中文GB2312编码中,汉字区域的第一个字节位于0xB0到0xF7,第二个字节位于0xA1到0xFE,把这段区间按双字节解码就能拿到全部汉字:

def get_gb2312_hanzi(): result = [] for hi in range(0xB0, 0xF8): for lo in range(0xA1, 0xFF): try: ch = bytes([hi, lo]).decode('gb2312') except UnicodeDecodeError: continue if '\u4e00' <= ch <= '\u9fff': result.append(ch) return result chars = get_gb2312_hanzi() print(len(chars)) # 6763 print(''.join(chars[:20])) # 啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘

这段脚本利用Python的gb2312解码,把涵盖的汉字全部跑出来,然后只保留落在CJK统一表意文字区间的字符,避免混入各种全角符号和制表符。跑完之后把结果写到文本里:

all_chars = ''.join(get_gb2312_hanzi()) with open('tmp_charset.txt', 'w', encoding='utf-8') as f: f.write(all_chars)

文件编码一定要指定UTF-8。Windows记事本另存为的时候也记得选“UTF-8”,而不是“ANSI”。GB2312本身就是中文国家编码标准之一,用这6763个字覆盖绝大多数日常文案绰绰有余,只有极少数人名地名里的生僻字会漏。

2.3 途径三:从项目实际语料里提取

如果项目文案已经形成一定规模,比如本地化表、剧情文本、剧情配置都堆在那了,就没必要靠主观猜测收录哪些字。直接写个脚本把所有文本扫一遍,统计字符频次,然后按频次取前N个作为字符集:

import glob from collections import Counter counter = Counter() for path in glob.glob('./Texts/**/*.txt', recursive=True): with open(path, encoding='utf-8') as f: counter.update(ch for ch in f.read() if ch.strip()) chars = ''.join(ch for ch, _ in counter.most_common(2000)) with open('runtime_charset.txt', 'w', encoding='utf-8') as f: f.write(chars)

这个脚本的价值在于,它能直接为你发布前的子集化提供依据。开发期你可以拿它生成一个Top 2000字文件作为主字符集,等到正式版资源优化时再压到Top 800甚至Top 1200。实际项目里剧情文本的字频分布挺有意思,经常前800个字就能覆盖95%的用字,剩下的都是角色名和地名。

2.4 字符集文件的基本格式要求

无论你最终用哪种途径拿到字符集,落到文本文件时规则只有一条:一行接一行直接写字符,不要逗号分隔,不要空格分隔,不要引号包裹。比如:

的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电动里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府称太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞” 注意这里演示的是常用字连排的样子,你自己生成的时候直接存储就行。**不要在文件里用空格把每个字隔开**,因为空格本身也是合法字符,会被当成一个独立字形加进图集。虽然多一个空格字形没多大影响,但会污染统计和排查。 ### 3. Font Asset Creator里这些参数,每一个都决定成败 ### 3.1 打开工具与导入字符集 在Unity菜单栏里找到`Window > TextMeshPro > Font Asset Creator`,这是TMP自带的字体资产生成工具。把中文字体文件拖到Source Font File栏。选字体文件有一点讲究:要做简体中文,最好直接选覆盖简体中文字符的字体文件,别拿一个只有简体但缺字严重的窄字符集字体去顶。 Character Set那里选择`Characters from File`,下方会出现Import Characters按钮,点它选择你的字符集文本。导入之后,工具会把这批字符转换成待烘焙的字符列表。此时你可以滚动窗口看有没有明显异常,比如乱码方块、空白条目。如果字符集文件编码错了,这里通常一眼就能看到一堆问号或者花花绿绿的替换符。一旦发现,立刻回去检查文件编码,别往下走。 ### 3.2 图集尺寸与字形采样分辨率 然后是Font Size、Padding、Atlas Resolution这三个参数。它们表面上是三个独立数值,实际上相互制约: - **Font Size(Sampling Point Size)**:决定每个字形被栅格化时的采样点大小,不是UI显示字号。开发调试期我一般取90到120。如果UI里大量使用大号标题,可以取到160左右。采样点太小,字形在Canvas上拉伸时边缘发虚;采样点太大,单个字形占的图集面积激增,一页图集放不下几百个字。 - **Padding**:字形周围的留白像素,用来防止相邻字形纹理互相渗色。中文笔画密、结构复杂,至少给6像素,我习惯直接给8。给太小,UI上文字边缘会出现毛糙的彩色边,尤其抗锯齿开启后特别明显。 - **Atlas Resolution**:图集纹理尺寸。3500个常用汉字,在Font Size=90、Padding=6的情况下用2048x2048勉强可以塞下但会非常紧凑;保险起见用4096x4096。如果你导入6763个全字库,4096一张图集也可能放不下,记得开启Multi Atlas Texture开关,允许生成多页图集。 | 字符规模 | 建议Atlas Resolution | 预期页数 | | --- | --- | --- | | 约3500个常用汉字 | 2048x2048 或 4096x4096 | 1页 | | 约6763个GB2312汉字 | 4096x4096 | 1至2页 | | 一倍以上CJK字符 | 4096x4096 + Multi Atlas | 多页 | ### 3.3 打包方式与最终生成 Packing Method有Fast和Optimum两个选项。Fast生成速度快但图集空间利用率略差,Optimum会花更长时间换来更紧凑的排列。开发期无所谓,我直接用Fast,省时间。生成之后得到一个`.asset`文件,存放位置建议单独建一个`Fonts/TMP`目录,别跟场景里的材质球混在一起。 生成过程中如果窗口底部提示“Some characters were not included in the generated Atlas”,要么是图集尺寸不够,要么是Padding过大导致空间不足。这时候优先把Atlas Resolution抬高一档,或者降低采样点尺寸,重新生成。 生成后还有一个容易忽略的地方:材质球。TMP字体资产会自带材质,你可以在运行时看到字体资产的材质引用了图集纹理。如果在UI上发现某些字显示为半透明或者颜色怪异,去检查材质球的Shader,通常改成`TextMeshPro/Distance Field`系列即可。 ### 3.4 空格和标点不要遗漏 做中文字符集时,很多人只盯着汉字,结果生成完UI上英文和数字正常、汉字也正常,就是逗号和句号的位置全空了。原因就是字符集文件里只放汉字,没放中文标点和ASCII标点。我的字符集文件通常按这个顺序组织: 1. ASCII可见字符(32到126),一段脚本就能追加; 2. 常用中文标点:,。!?;:“”‘’、——……《》【】()·; 3. 空格和全角空格U+3000; 4. 简体汉字部分。 尤其全角空格U+3000,中文排版经常出现,不收录它,文本里段首缩进就会显示成空格方块,非常扎眼。中文省略号和破折号是六连点/双横线形态,属于独立Unicode字符,也容易漏,建议在字符集文件里显式加进去。 ### 4. 一套全量中文字库到底多重:开发版与上线版的差距 ### 4.1 静态图集的内存账 静态TMP字体资产把字符烘焙成图集纹理,内存占用主要看图集尺寸。一个4096x4096的RGBA图集,光纹理就是4096乘以4096乘以4字节,算下来64MB。这还只是一页。如果字符串集超过一页,比如6763全字库加上ASCII和标点,生成了两页4096图集,就是128MB。 对一个正在进行玩法开发的工程来说,这个内存开销其实不算致命,因为开发机上性能余量足、调试环境宽松。但放到手机端,特别是中低端机型,128MB只为文字渲染服务显然太奢侈。这就是标题里“仅用于开发”的实用含义:**开发阶段你用它换稳定、换效率,正式包再想办法瘦身**。 ### 4.2 动态字体:从设计到运行时的行为差异 把TMP字体资产的Character Mode从Static改成Dynamic,就不会再烘焙任何字形到图集。运行时TMP遇到一个字符,先去字体系统里找对应字形,找不到才临时栅格化并加入动态图集。动态字体的好处是:Asset文件极小,内存中图集只保留实际用到的字符,界面文案越少的场景,内存占用越低。 坏处也很明显:首次出现某个字符时会有一次栅格化开销,连续滚动显示大量新字符时可能出现瞬时卡顿;另外动态模式依赖目标系统里存在对应字体文件,比如你的中文字体资产如果指向的是编辑器机器上的某个系统字体,真机上没装这个字体,TMP就找不到字形,渲染出来还是方框。所以做动态字体方案,要么在启动时手动把打包进App的字体文件加载给TMP,要么确保目标设备系统字体覆盖足够。 ### 4.3 我的开发与上线过渡策略 我现在的项目基本走这样一套流程: - 开发期:生成一份覆盖3500常用汉字或GB2312全字库的静态字体资产,UI绑定的都是这套,美术和策划随便写文案,缺字率极低。 - 功能稳定后:跑一次语料统计脚本,把当前所有本地化文案、剧情表、UI多语言表全部收集起来,提取实际出现的字符集合,生成一份“最小静态字符集”。 - 上线阶段:优先用最小静态字符集重新生成一个静态资产。如果最小集只有800到1000字,2048图集就够了,内存占用降到16MB。若项目对包体极度敏感,再切动态,但必须把字体文件打进App并写一段启动加载逻辑。 这套组合的好处是,不需要在开发期频繁处理Fallback链,不需要临时去调TMP的Dynamic开关,也不需要为“为什么真机上这个字漏了”重新返工。静态集兜底,动态集补充,两边职责清晰。 ### 4.4 主字体与Fallback字体之间怎么配合 TMP的Fallback Font Assets机制允许主字体找不到某个字符时,去备选字体资产里继续找。中英文混排项目里,一个常见配置是:主字体放中文静态资产,Fallback放英文字体或包含特殊符号的字体。注意,最好不要反过来把中文放到Fallback里。因为英文文本里中文字符出现频率低,Fallback偶尔查一次无所谓;但中文文本是主力,每个汉字都先命中主字体是性能最优方案。一旦把中文字体放到Fallback,每次渲染一堆汉字都要走失败路径,性能损失非常明显。 另外Fallback链上字体资产不要挂太多,理论上每增加一级,查表深度就加一层。理想情况是:主中文字体覆盖95%以上字符,Fallback只兜底表情符号、特殊单位、生僻人名。 ### 5. 六个最容易反复返工的具体坑位,能避开就避开 ### 5.1 字符集文件编码:BOM和ANSI的埋伏 字符集文件用UTF-8编码已经是共识,但Windows环境下经常出妖蛾子。记事本早期版本默认保存为ANSI,把文件拖进Font Asset Creator之后,Character List里中文直接变成乱码;某些编辑器保存UTF-8时会自动带BOM头。BOM本身不一定是致命问题,TMP多数版本能读,但为了不赌人品,建议统一存成UTF-8无BOM。VS Code右下角可以改编码格式,选择一个好编辑器做这件事比用记事本省心。 ### 5.2 把字符集做成“每行一个字符”的列表文件 有人习惯把字符集整理成Excel列表,每行一个字,导出时又拼上逗号或者制表符。结果导入TMP后,逗号被当成英文字符、Tab被当成空白字符,图集里莫名其妙多了很多奇怪的glyph。字符集文件格式就是单纯的字号流,一行排多个字没关系,但是不要加任何分隔符。如果你希望以后能快速对比两份字符集差异,可以单独在工程目录里维护一份带换行的原始字表,生成给TMP的文件前用脚本去掉换行即可。 ### 5.3 中文缺字在UI上表现成“空方块”,但原因要分情形 同样是空方块,原因不同。如果字体资产里压根没这个字符,TMP会显示为空心方框;如果字体资产里有字符,但字体的字形轮廓是空的,比如某些残缺字体文件,TMP显示的就是实心方块或乱码。前者靠扩充字符集解决,后者要换字体文件。怀疑是字体文件问题的时候,直接用系统字体生成一次同字符集资产对比,很快就能定位是字体文件坏了还是TMP配置不对。 ### 5.4 Font Size取太高导致图集爆页 有的同学为了让文字更清晰,Sampling Point Size猛拉到200甚至300。这在文字量少的场景没问题,但在6500多字的全字库场景下,每个字形占的面积大幅增加,可能把图集页数从1页推到4页,内存瞬间飙升。UI上TMP字体是Distance Field风格,122到160的采样已经能保证很好的清晰度,没必要刻意堆高。 ### 5.5 发布前忘了检查目标机型的系统字体 动态字体方案最容易踩的坑就是编辑器里一切正常,真机上部分设备显示方框。原因很简单,编辑器开发机装了一堆字体,真机没有。如果项目UI字体要求比较高,最好在App包里放一份授权允许分发的字体文件,运行时把它创建为TMP Font Asset再赋给组件,而不是依赖系统环境。更稳妥的做法仍然是使用最小静态字符集,完全不依赖系统字体。 ### 5.6 标点独立字符带来的缩进和排版问题 中文排版用到中文引号“”、破折号——、省略号……,这些是独立Unicode字符,英文ASCII里没有。很多字符集文件作者只关心汉字,导致UI文案里“他说:“你好”——”这种句子整体缺字。排查的时候往往看不出问题,因为汉字都正常显示,就是引号位置空着。字符集文件里显式追加这些标点,并把它写进项目规范,后续新人接手也不会漏。 ### 6. 我自己的字符集管理清单与一个高效工作流 ### 6.1 文件和目录层面的约束 我在每个Unity工程里都会建一个`Tools/TextMeshProCharset/`目录,里面固定放三份文件: - `dev_full_charset.txt`:开发用完整字符集,包含GB2312全字库加中文标点和ASCII可见字符; - `dev_common_charset.txt`:3500常用字加标点的版本,用来给普通迭代分支用; - `generate_runtime_charset.py`:扫描所有项目文案并统计字符频次的脚本。 每次调整字符集,我都是从这三份文件出发,而不是临时去网上下载。这样版本管理清晰,团队成员之间一旦出现缺字问题,直接把字符集文件diff一下就知道谁少了什么字。 ### 6.2 为什么我建议至少准备两套资产 有些团队图省事,从开发到上线一直用一套全字库静态资产。只要包体不急、内存还能扛,确实可以。但只要项目开始优化,这套资产必然要动。与其在上线前急急忙忙学怎么切动态、怎么做子集,不如开发期就顺手把流程打通:一套全量静态资产给日常开发,一套最小静态或者动态资产给发布构建。两套资产之间切换的成本很低,无非是替换UI引用的字体资产引用而已。 我在实际项目里的体会是,文字渲染问题最折磨人的从来不是技术难点高,而是排查链路长、影响因素多。字符集文件、字体文件、TMP参数、目标环境随便一个变了,UI上就是一片方框。如果你也正在被TextMeshPro中文缺字问题困扰,按我上面这套准备字符集文件、规范生成参数、明确开发与发布资产区分的方法走一遍,大概率能少走很多弯路。 最后再分享一个实用小技巧:字符集文件不要迷信网上打包好的“常用字大全”,自己用脚本从项目语料里统计出来的结果最可靠。字符集这个东西,覆盖得精准比覆盖得广泛更有价值。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 18:16:00

迪思杰RealSync异构数据库实时同步部署实战与避坑指南

简介&#xff1a;迪思杰RealSync部署手册面向数据库运维人员与系统集成工程师&#xff0c;聚焦Oracle环境下数据复制软件的安装与配置&#xff0c;帮助读者在源端与目标端完成RealSync的完整部署。手册以doc文档形式呈现&#xff0c;压缩包内共1个文件&#xff0c;大小约129KB&…

作者头像 李华
网站建设 2026/10/13 18:14:37

MuAsk:离线桌面级Text2SQL工具实战指南

1. 项目概述&#xff1a;为什么一个桌面级 Text2SQL 工具值得你花十分钟装上试试&#xff1f;“沐问 MuAsk”这四个字刚出现在我 GitHub Star 列表里时&#xff0c;我第一反应是——又一个披着中文外衣的 LLM 前端&#xff1f;点开仓库主页&#xff0c;看到PySide6SQLite本地模…

作者头像 李华
网站建设 2026/10/13 18:11:23

代码整理插件 ponytail:自动合并重复代码,让项目结构清爽如马尾

ponytail 这名字第一次出现在我面前&#xff0c;是在一个前端交流群里。有人截图说项目里的重复代码被一个命令行工具自动“扎”成了整齐的一束&#xff0c;下面附了张 diff 图&#xff0c;改动量不大&#xff0c;但读起来舒服太多。当时我正被一个老项目的“三不管地带”折磨—…

作者头像 李华
网站建设 2026/10/13 18:08:51

智能体基础设施Agent Infra实战:普通工程师的入场攻略

前阵子和几个做AI应用的朋友聊技术选型&#xff0c;聊到后面基本都会落到同一个问题&#xff1a;各家模型能力差距越来越小&#xff0c;API价格一降再降&#xff0c;为什么有的产品体验明显更聪明&#xff0c;有的却像个玩具&#xff1f;答案往往不是模型&#xff0c;而是藏在背…

作者头像 李华
网站建设 2026/10/13 18:08:41

多步流程越权漏洞:从原理剖析到检测修复实战指南

权限滥用几乎是漏洞清单里最容易被低估的一类&#xff0c;尤其是埋在长流程里的越权漏洞&#xff0c;往往要在特定步骤组合下才触发。梳理一个典型的订单业务&#xff1a;整个取消订单闭环分四步——选择地址、确认商品、填写发票、提交取消申请。前三步都正常&#xff0c;第四…

作者头像 李华
网站建设 2026/10/13 18:08:35

用NumPy手写神经网络:从反向传播到MNIST分类实战

1. 核心设计&#xff1a;为什么用NumPy手写神经网络接触深度学习这么久&#xff0c;我一直觉得有一件事是绕不过去的&#xff1a;你天天用框架调接口&#xff0c;但真的让你从零推导一遍反向传播&#xff0c;能把每个参数的梯度算对的人并不多。这个项目用纯NumPy在MNIST上实现…

作者头像 李华