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

发布时间:2026/10/13 18:18:46

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, encodingutf-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, recursiveTrue): with open(path, encodingutf-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, encodingutf-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 SizeSampling Point Size**决定每个字形被栅格化时的采样点大小不是UI显示字号。开发调试期我一般取90到120。如果UI里大量使用大号标题可以取到160左右。采样点太小字形在Canvas上拉伸时边缘发虚采样点太大单个字形占的图集面积激增一页图集放不下几百个字。 - **Padding**字形周围的留白像素用来防止相邻字形纹理互相渗色。中文笔画密、结构复杂至少给6像素我习惯直接给8。给太小UI上文字边缘会出现毛糙的彩色边尤其抗锯齿开启后特别明显。 - **Atlas Resolution**图集纹理尺寸。3500个常用汉字在Font Size90、Padding6的情况下用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. 空格和全角空格U3000 4. 简体汉字部分。 尤其全角空格U3000中文排版经常出现不收录它文本里段首缩进就会显示成空格方块非常扎眼。中文省略号和破折号是六连点/双横线形态属于独立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.txt3500常用字加标点的版本用来给普通迭代分支用 - generate_runtime_charset.py扫描所有项目文案并统计字符频次的脚本。 每次调整字符集我都是从这三份文件出发而不是临时去网上下载。这样版本管理清晰团队成员之间一旦出现缺字问题直接把字符集文件diff一下就知道谁少了什么字。 ### 6.2 为什么我建议至少准备两套资产 有些团队图省事从开发到上线一直用一套全字库静态资产。只要包体不急、内存还能扛确实可以。但只要项目开始优化这套资产必然要动。与其在上线前急急忙忙学怎么切动态、怎么做子集不如开发期就顺手把流程打通一套全量静态资产给日常开发一套最小静态或者动态资产给发布构建。两套资产之间切换的成本很低无非是替换UI引用的字体资产引用而已。 我在实际项目里的体会是文字渲染问题最折磨人的从来不是技术难点高而是排查链路长、影响因素多。字符集文件、字体文件、TMP参数、目标环境随便一个变了UI上就是一片方框。如果你也正在被TextMeshPro中文缺字问题困扰按我上面这套准备字符集文件、规范生成参数、明确开发与发布资产区分的方法走一遍大概率能少走很多弯路。 最后再分享一个实用小技巧字符集文件不要迷信网上打包好的“常用字大全”自己用脚本从项目语料里统计出来的结果最可靠。字符集这个东西覆盖得精准比覆盖得广泛更有价值。
延伸阅读

更多相关文章

2026/10/13 18:13:44

降低渲染分辨率为何让画面变糊?

一、先分清两个分辨率屏幕分辨率:屏幕上真实的格子数,固定不变,例如 19201080渲染分辨率:游戏实际计算、绘制的画面大小,可以由游戏自己决定平时两者一样大,游戏直接画到屏幕上。 降低渲染分辨率时&#xf…

2026/10/13 18:13:44

1920×1080图片能铺满手机吗?真相揭秘

简短回答 理论上是的:如果游戏全屏运行,scale 1,那么:1 个图片像素 1 个屏幕像素图片 19201080┌──────────────────────┐│ │ 刚好铺满,不放大、不缩小│ …

2026/10/13 18:13:44

MySQL 8.0 管理员实战指南:从安装配置到锁表排查与性能调优

简介:这份《MySQL 8.0 for Database Administrators Student Guide》是Oracle University官方出品的数据库管理员培训教材,面向正在学习或备考MySQL 8.0的DBA、运维工程师及数据库方向的学生,帮助其系统掌握8.0版本在安装配置、用户权限、备份…

2026/10/13 22:55:27

水下管道YOLO检测实战:从数据集到ROV端侧部署

简介:本资源是面向海洋工程、水下基础设施智能巡检领域的YOLOv8目标检测实战项目,专为计算机视觉初学者与工业检测算法工程师设计,解决水下管道在复杂光照、低对比度、悬浮颗粒干扰等场景下的精准识别与定位难题。压缩包共2000个文件&#xf…

2026/10/13 22:55:27

最小绝对偏差(LAD):比最小二乘更稳健的回归方法

1. 什么是“最小绝对偏差”——一个被低估却异常实用的统计思想你可能在机器学习课上听过“最小二乘法”,知道它用平方误差来拟合直线;也可能在做回归分析时,发现模型对几个离群点特别敏感,预测结果一塌糊涂。这时候,有…

2026/10/13 22:50:26

模具水路清洗压力与频率匹配技术解析

1. 模具水路清洗的核心挑战模具水路系统在长期使用过程中,不可避免地会积累水垢、锈蚀物和微生物等污染物。这些沉积物会导致冷却效率下降、模具温度不均甚至水路堵塞等问题。根据我们注塑车间的实测数据,未及时清洗的模具水路内壁沉积物厚度达到0.5mm时…

2026/10/13 5:19:27

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/13 17:09:18

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/13 0:04:41

基于Triton部署YOLO目标检测:从训练脚本到线上服务

简介:这份资源面向希望将目标检测模型落地到生产环境的深度学习开发者与算法工程师,聚焦于用 Triton Inference Server 部署 YOLOv9 的完整实战方案,解决从训练权重到线上推理服务之间的工程化难题。压缩包共 16 个文件,约 886KB&…

2026/10/13 0:04:41

手把手教你云端部署Qwen-Image-2.1:从GPU选型到在线文生图服务

Qwen-Image-2.1 这个开源的图像生成模型,最近在内容创作和 AI 应用圈子里热度相当高。它属于通义 Qwen 系列里的视觉生成分支,让我眼前一亮的是它对中文长文本的理解能力——以前用那些老方案生成带中文文字的图,十个里九个是“乱码印刷体”&…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑