关于 fonts
面向 Decentraland 场景开发的字体使用指南,说明内置字体、自定义 .ttf 字体及 fontSrc 的版本要求,并涵盖移动端回退和字体测试。
主要功能
- 01说明内置字体与文本组件字段
- 02检查 SDK 版本后使用 fontSrc
- 03为自定义字体设置内置回退
- 04说明字体文件与许可要求
- 05涵盖客户端差异及测试方式
适用场景
- 01为 Decentraland 场景配置自定义字体
- 02确保移动端回退字体下的文字可读
- 03排查字体未加载或格式不受支持
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ decentraland/ sdk-skills --skill fonts安装后在 Claude Code 中输入/fonts确认已加载。
详细介绍
Decentraland 中的字体
每个文本组件都有两个字体字段:
| 字段 | 接受的值 | 组件 |
|---|---|---|
font | 内置字体。对于 TextShape / UiText:Font.F_SANS_SERIF(默认)、Font.F_SERIF、Font.F_MONOSPACE。对于 React-ECS 属性:'sans-serif'(默认)、'serif'、'monospace' | 全部 |
fontSrc | 场景内容中的 .ttf 文件路径,相对于场景根目录 | TextShape、UiText、UiInput、UiDropdown,以及 React-ECS 的 Label、Button、Input、Dropdown 和 UiEntity uiText |
仅有这三种内置字体。除此之外没有其他字体名称列表。
规则:编写 fontSrc 前先检查 SDK 版本
fontSrc 需要 @dcl/sdk 7.31.0 或更高版本。更早的版本中不存在此字段:它会导致 TypeScript 错误,即使强行绕过类型检查,SDK 也不会将其发送给浏览器。
编写前,请从 node_modules/@dcl/sdk/package.json(或 package.json)读取已安装的版本。如果场景使用的是旧版本,请升级 @dcl/sdk,或继续使用内置的 font。切勿修改 node_modules 中 SDK 生成的序列化器,否则下次安装时改动就会丢失。
规则:始终将 font 与 fontSrc 一起设置
当自定义字体无法使用时,font 会作为回退字体:在移动端始终如此;在桌面端,则会持续使用回退字体,直到字体的资源包准备就绪(参见“测试”)。选择最接近的内置字体,让回退后的显示效果仍符合预期:衬线字体或展示字体使用 'serif',代码或终端字体使用 'monospace',其他情况使用 'sans-serif'。
规则:移动应用会忽略 fontSrc
Decentraland 移动应用不支持自定义字体:它始终使用 font 回退字体渲染,且仅支持 Noto Sans、Noto Serif 和 Noto Sans Mono 及其粗体变体(截至 2026-11 的观察结果)。所有使用 fontSrc 的场景都必须确保使用回退字体时仍然显示正确且布局合适:
- 谨慎选择回退字体(见上一条规则),并且在为移动端目标设计文本密集型 UI 之前就选好,而不是设计后再选:一次复盘发现,有三个屏幕是在读到这条规则之前,围绕展示字体设计的。
- 切勿依赖自定义字体的宽度,让文本适应固定尺寸的框。调整框的尺寸,确保回退字体也能容纳文本。
- 切勿只通过自定义字体的外观来传达含义(例如图标字体、符号字体或“手写”线索)。在移动端,它们会变成普通文本。
用户添加自定义字体时,请告知他们该字体不会在移动端显示。
必须在移动端正常显示的展示字体需要使用图像文本:按字形制作位图字体,每个字符使用一个 UiEntity,将字形绘制为图集中的 uvs 裁剪区域(build-ui → references/ui-components.md → Texture UVs)。它在所有客户端上的渲染效果都相同,但有两项代价:
- 每种颜色需要一个图集。 移动端会忽略
uvs裁剪区域上的uiBackground.color色调和透明度({baseDir}/../sdk-scenes/references/mobile-parity.md),因此需要预先为每种变体着色,并将阴影和描边烘焙到字形图案中。 - 节点数量。 每个字形都是一个节点,叠加副本会使节点数成倍增加:用 17 个叠放的
Label模拟描边,会让一个屏幕降至约 6 FPS;而将阴影烘焙到字形图案中,则让另一个屏幕从约 23 FPS 提升至约 45 FPS。图像文本仅用于标题和短标签。
规则:自定义字体是场景中的 .ttf 文件
fontSrc 是文件路径,而不是字体名称。支持自定义字体,但用户指定的字体(“使用 Playfair Display”)只有在其 .ttf 文件位于场景文件夹中,且 fontSrc 指向该文件时才能使用。当用户想使用自定义字体,而场景中还没有对应的 .ttf 文件时,告诉他们支持自定义字体,并请他们提供 .ttf 文件,或提出为他们下载,但须先征得确认,和下载任何资源一样(sdk-scenes → Agent 行为准则,第 2 项)。无论哪种方式,字体许可证都必须允许将字体随场景一同再分发:大多数 Google Fonts 使用 SIL Open Font License,该许可证允许这样做。
Unity explorer 不支持其他任何格式。所有其他值都会在其中静默回退到 font:
fontSrc 值 | Unity explorer | Bevy explorer |
|---|---|---|
场景内容中的 .ttf | 可以,需先有对应的资源包(已部署,或使用会在本地转换字体的桌面预览) | 可以 |
带有 CFF 轮廓的 OpenType(通常为 .otf)、字体集合(.ttc)、Web 字体(.woff、.woff2) | 回退 | 未记录——请勿依赖 |
大于 16 MB 或字形轮廓极其复杂的 .ttf | 回退(转换器拒绝处理) | 未记录 |
.ttf 的 URL | 回退 | 可以 |
字体系列名称('Playfair Display') | 回退 | 可以 |
| 缺失文件、无效文件、空字符串 | 回退 | 回退 |
协议的文档注释会显示在编辑器工具提示中,其中写道字体系列名称可以使用。但这仅适用于 Bevy:Unity 有意移除了外部字体(出于安全、代码体积和运行时字形栅格化方面的考虑)。切勿在 fontSrc 中填写字体系列名称或 URL。
桌面客户端拒绝某个 fontSrc 值时,会记录一行 SDK_FONTS,说明原因:URL 会显示警告(font_src "…" cannot be an external URL);任何非场景文件的值(包括字体系列名称)都会显示普通日志行(font_src "…" is not a content file of the scene …)。字体回退时,请先检查控制台,包括非警告日志行,再进行其他操作。
向场景添加字体
- 将
.ttf放在assets/fonts/下,例如assets/fonts/BungeeShade-Regular.ttf。请使用静态.ttf。如果某个字体系列只提供可变字体,请使用其静态实例。 - 将字体许可证放在字体文件旁边(
assets/fonts/BungeeShade-OFL.txt)。部署会将该文件发布到公共内容服务器,因此许可证必须允许再分发和嵌入。OFL 和 Apache 字体许可证允许这样做。大多数商业桌面字体许可证则不允许。 - 使用相对于场景根目录的路径引用字体,方式与纹理的
src相同:fontSrc: 'assets/fonts/BungeeShade-Regular.ttf'。
一个文件对应一种字型。若标题使用粗体、正文使用常规字重,请随场景一并提供这两种字重,并让每段文本分别指向对应的文件。
成本:使用相同 fontSrc 的所有文本实体共享同一份已加载副本,因此在场景中重复使用一种字体的成本不高于使用一次。每个不同的字体文件都会生成独立的资源包(约 700 KB,包含两个纹理图集),并占用其自身的内存纹理。场景中请控制在一到两个字体文件。
3D 文本(TextShape)
import { engine, Transform, TextShape, Font } from '@dcl/sdk/ecs'
import { Vector3, Color4, Color3 } from '@dcl/sdk/math'
const sign = engine.addEntity()
Transform.create(sign, { position: Vector3.create(8, 3, 8) })
TextShape.create(sign, {
text: 'OPEN',
fontSize: 6,
textColor: Color4.White(),
outlineColor: Color3.Black(),
outlineWidth: 0.1,
font: Font.F_SERIF,
fontSrc: 'assets/fonts/AbrilFatface-Regular.ttf'
})
// Switching font at runtime is a plain field write
TextShape.getMutable(sign).fontSrc = 'assets/fonts/BungeeShade-Regular.ttf'
UI 文本(React-ECS)
<Label
value="Score: 100"
fontSize={24}
font="sans-serif"
fontSrc="assets/fonts/BungeeShade-Regular.ttf"
uiTransform={{ width: 240, height: 40 }}
/>
<Button value="Play" variant="primary" font="sans-serif" fontSrc="assets/fonts/BungeeShade-Regular.ttf" />
<Input placeholder="Your name" font="serif" fontSrc="assets/fonts/PTSerif-Regular.ttf" />
<Dropdown options={['Easy', 'Hard']} font="serif" fontSrc="assets/fonts/PTSerif-Regular.ttf" />
<UiEntity uiText={{ value: 'Coded-only text bag', font: 'monospace', fontSrc: 'assets/fonts/AzeretMono-Medium.ttf' }} />
可编辑 UI(build-ui):手动在元素上填写 fontSrc。Creator Hub UI Designer 既不会显示也不会编辑此属性,但保存时只会合并其更改的 props,因此手动填写的 fontSrc 会保留(Prettier 可能会重新缩进该行)。其画布会渲染 font 回退效果。仅仅为了使用自定义字体,无需将 UI 切换到代码模式。
测试自定义字体
桌面客户端(Unity explorer)只会从资产包加载场景字体,绝不会从原始字体文件加载。因此,自定义字体只会在存在资产包的地方显示:
- 本地预览(
npm run start或 Creator Hub Play,无需标志):Desktop Explorer 会在场景加载前自行转换场景资产,包括.ttf字体,因此字体会显示。只有.ttf会被转换:其他格式的字体会继续使用font回退字体。如果字体仍显示回退字体,说明转换器没有运行,或无法为此场景提供服务;Explorer 调试菜单中的 AB 面板会说明原因(optimize-scene → 本地资产包预览)。 - **已部署的场景:**服务器完成资产包转换后,字体才会显示。在此之前会显示回退字体。在字体转换功能推出前部署的场景,只有重新部署后才会获得字体资产包。
- Bevy explorer 会直接加载
.ttf,因此其预览无需转换即可显示字体。 - **移动应用:**始终显示回退字体(见移动端规则)。
- 在用户报告“我的字体不起作用”之前,先告知他们适用上述哪种情况。
- 预期行为的参考场景是 decentraland/sdk7-test-scenes 中的
scenes/84,-6-font-src(zone 上的 realmsdk7testscenes.dcl.eth)。它涵盖了每种回退情况、组件间共享字体、重新创建组件以及场景边界。
字形与易读性
- 自定义字体只能绘制其包含的字符。缺失的字符会在词语中间以内置无衬线字体绘制,看起来会很不协调。对于重音字母、西里尔文、CJK 等字符,请选择包含相应文字的字体。
- 字体的资产包已包含 ASCII、智能标点(引号、破折号、省略号、项目符号)、€ 和常见的 Latin-1 字母。字体包含的任何其他字符(西里尔文、希腊文、CJK……)都会在首次出现时渲染,这会造成短暂卡顿。对于这些文字,应尽早显示(例如在加载或介绍画面中),不要等到关键时刻才显示。
- 任何文本值中都不能使用 emoji,无论使用什么字体。Unity explorer 没有 emoji 字形(见 build-ui)。
- 装饰性字体和展示字体仅用于简短标题和标牌。正文以及移动时需要阅读的内容,应使用 build-ui(UI)和 advanced-rendering(3D)中规定尺寸下的朴素字体。
- 无论是内置还是自定义等宽字体,都无法让
TextShape中的空格按列对齐。每列使用一个TextShape(见 advanced-rendering)。
交叉引用
- build-ui:Label、Button、Input 和 Dropdown 的属性、尺寸及禁止使用 emoji 的规则。
- advanced-rendering:
TextShape的放置、方向、轮廓和对齐。 - migrate-sdk6-to-sdk7:SDK6 的
font名称映射到内置的Font枚举,自定义 SDK6 字体映射到fontSrc。 - deploy-worlds / deploy-scene:部署场景,以便服务器转换字体。
- optimize-scene:资产包转换及其对加载时间的影响。