平台工程

导出运行时无系统字体回退律 · CJK 豆腐块 v1

入档:2026-07-24 来源:LastStand(Godot 4.7 FPS)Web 导出验证 —— 设置界面档位值中文全变豆腐块,桌面版一切正常 验证状态:✅ 已定位并修复(改字体回退后 Web 端中文恢复) 性质:导出/嵌入式运行时的字体坑,跨引擎通用(Godot Web/移动、任何 WASM/无系统字体沙箱)


一句话律

桌面端会用操作系统字体自动补全字体里缺失的字形,导出到 Web/移动/嵌入式运行时后没有系统字体可借,凡是用「不含目标字形的字体」渲染的文本一律变豆腐块(□)。 跨字体、跨语言的项目必须给每个「主力字体」显式挂 fallbacks,把桌面那份隐式 OS 回退固化进导出——桌面测不出来,只有到目标运行时才现形。


踩坑现场

LastStand 首次 Web 验证导出,进游戏 → 设置 → DISPLAY:

桌面版这些值中文完全正常,只有 Web 出问题。

根因:值由 CustomCycleButtondraw_string(font, …) 绘制,其 font 硬编码为 JetBrains Mono(纯拉丁等宽,无 CJK 字形)。标签用的是 NotoSansSC(自带中文)所以没事。

为什么桌面看不出来:Godot 桌面端 TextServer 找不到字形时会回退到操作系统字体(Windows 有一大堆中文字体),悄悄把「窗口」画出来了。Web 导出的运行时没有任何系统字体可借,回退链一空,直接吐豆腐。这层 OS 回退是隐式的、不进包的,所以桌面自测永远测不出这个 bug


修法:把隐式 OS 回退固化成显式 fallbacks

给每个可能用来画非本字符集文本的「主力字体」显式挂 fallback。LastStand 的做法——4 个拉丁 FontVariation(JBMono ×2、Oswald ×2)各加一条 NotoSansSC 回退:

[gd_resource type="FontVariation" load_steps=3 format=3]
[ext_resource type="FontFile" path="res://assets/fonts/JetBrainsMono-VF.ttf" id="1_ttf"]
[ext_resource type="FontFile" path="res://assets/fonts/NotoSansSC-VF.ttf" id="2_noto"]
[resource]
base_font = ExtResource("1_ttf")
fallbacks = Array[Font]([ExtResource("2_noto")])
variation_opentype = { "wght": 500 }

要点:


配套坑(同场次)

同一次 Godot Web 导出还踩了两个,一并记:

  1. headless --export 的 “configuration errors” 不吐细节 —— 命令行只报「因配置错误无法导出」,不说缺什么。我据此手搓猜了半天「preset 字段不全」,全错。真相是编辑器 GUI 一打开就逐条红字点破(此处是「目标平台需要 ETC2/ASTC 纹理压缩」)。教训:卡在笼统配置错误时,别在 headless 里猜字段,开一次编辑器 GUI 是最快的诊断器。参见 交付前实测证伪律_v1 的反面——我把「诊断不足」当成了「结论确定」。
  2. Web/Compatibility 渲染要求纹理 ETC2/ASTC 压缩 —— 项目需开 textures/vram_compression/import_etc2_astc=true,会给每张贴图再存一份 ETC2 变体(与桌面 s3tc 并存)。这是上面那条笼统报错的实际内容。

如何使用 / 判断

出现「桌面正常、导出后部分文字变豆腐块」时,先怀疑字体缺字形 + 运行时无系统回退,而不是编码/乱码:


关联文档


版本

类型/平台工程主题/字体