教程:文本组件
本教程将介绍文本组件的格式和相关应用。
在Java版25w02a,命令中的文本组件改用SNBT来表示。在Java版1.21.4正式版及以前,JSON是文本组件的标准书写格式。
用途[编辑 | 编辑源代码]

Minecraft使用文本组件展示大部分文本内容。
你在告示牌、书与笔中书写一些文字,这些文字一般都是纯文本。但是从技术上讲,这些文字实际上是文本组件,对其数据稍加修改就可以让文字不再是简单的纯文本。
通过铁砧给物品或者实体命名,这个名字实际上也是文本组件,修改其数据即可使物品或实体的名字带有格式。
进度的名称和描述也使用了文本组件,它使用了翻译标识符。如果修改你的游戏语言,进度的名称和描述也将随之改变,这是由于它是由文本组件定义了翻译标识符,然后对应到游戏资源文件相应语言的相应内容。通过修改文本组件,我们也可以让告示牌、物品或实体的名称、书的内容也能够随游戏语言而变化。
可见,文本组件在游戏内非常普遍。要用命令修改实体的名称、告示牌或书的内容,或者设置记分项或boss栏的名称,你可以直接输入字符串。但如果你想让其拥有更多样式的话,便必须要有一点关于文本组件的知识。
但也请注意:小部分文本(例如世界名称、服务器的MOTD)尚未使用文本组件。这些文本只能使用格式化代码来自定义格式。
使用字符串[编辑 | 编辑源代码]
在文本内容两段加上双引号(注意是英文的半角双引号),即可形成一个字符串:
"Welcome to Minecraft!"
它就是一个单独的字符串文本组件,效果如下:
Welcome to Minecraft!
如果你只是为了自定义文本且没有其他需求,你可以直接写成字符串的形式。
文本组件元素介绍[编辑 | 编辑源代码]
为了进一步自定义文本组件,你需要将其写成一个对象,并在里面使用元素。
以下介绍了所有可用的文本组件元素。
纯文本:text[编辑 | 编辑源代码]
纯文本是最简单、最基础的文本内容元素。
在对象中,可以使用text来显示特定的文本:
{
"text": "Welcome to Minecraft!"
}
效果:
Welcome to Minecraft!
如果只有text元素,那么它和字符串文本组件是等价的。
如果文本里出现了“"”或“\”,那么便需要在这些字符的前面加上反斜杠“\”来对其进行转义。其他有效的转义字符在文本里也是可用的。
{
"text": "\\u00A7会被转义成\"分节符\""
}
效果:
\u00A7会被转义成"分节符"
装饰文本[编辑 | 编辑源代码]
有时候我们要将文本渲染成特定的颜色,或者应用特定的格式。这时候以下元素便可以帮助我们渲染文本。
添加颜色:color[编辑 | 编辑源代码]
color用于指定对象内文本的颜色,其值可以为颜色名称或十六进制颜色代码。
以下为所有可用的文本值:
可用值 | 名称 | 前景色 | 背景色 |
---|
下面的实例会将“Welcome to Minecraft!”渲染为深红色:
{
"text": "Welcome to Minecraft!",
"color": "dark_red"
}
效果:
Welcome to Minecraft!
此外,你还可以用十六进制颜色代码来添加颜色。十六进制颜色代码使用"#<hex>"
的格式输入,字母的大小写不受限制。相比于文字值,十六进制代码可以指定绝大部分颜色,可以用于自定义颜色。
下面的实例会将“Welcome to Minecraft!”渲染为颜色代码为#1BDD00
的绿色:
{
"text": "Welcome to Minecraft!",
"color": "#1BDD00"
}
效果:
Welcome to Minecraft!
添加格式:bold、italic、underlined、strikethrough和obfuscated[编辑 | 编辑源代码]
bold(粗体)、italic(斜体)、underlined(下划线)、strikethrough(删除线,中划线)和obfuscated(随机字符)可以将文本设置为特定的格式。

这些元素都是布尔值,值为true
(应用样式)或false
(不应用样式,用于取消父对象已有的样式)。
这些元素可以同时使用,对应的格式将会叠加在一起。
下面的对象将会为文字设置加粗和倾斜效果:
{
"text": "The text with bold and italic",
"bold": true,
"italic": true
}
效果:
The text with bold and italic
obfuscated本质上和其他添加格式的元素相同,但它将会把文本变为会一直变化的随机字符。游戏只会选择与原文本等宽的字符作为其随机字符。
指定字体:font[编辑 | 编辑源代码]
font用于指定文本的字体,使用的字体需要在assets/<namespace>/font
内进行定义。若输入字体内不存在的字符,它将会显示为一个方框。字体可以通过资源包添加。

游戏内存在的字体有:
minecraft:default
:预设字体。注意,若开启“强制使用Unicode字体”,其中的字符依旧会变为Unicode字体。minecraft:uniform
:Unicode字体。开启“强制使用Unicode字体”后显示的字体。minecraft:alt
:标准银河字母。在附魔台上会使用此字体。minecraft:illageralt
:Minecraft Dungeons中使用的符文字体。
指定文本内容[编辑 | 编辑源代码]
纯文本是最简单的文本内容元素,但有时候我们希望它显示更丰富的内容,这时候我们可以使用别的文本内容元素。它们最终会输出为文本,输出的内容视元素及输入的值决定。一个对象内必须要含有一个文本内容元素。
记分项分数:score[编辑 | 编辑源代码]
score用于显示指定实体的记分项的分数。该元素是一个JSON对象。
对象score内必须要有参数name和objective。name是要显示分数的实体,其值可以为玩家名、目标选择器或*
(选择阅读这段文本组件的实体)。使用目标选择器时只能选择一个实体。objective是对应的记分项名。
{
"score": {
"name": "Jim",
"objective": "dummy"
}
}
这会显示Jim在记分项dummy上的分数。Jim并不一定需要在线,甚至不一定需要存在。如果Jim在dummy上没有分数,那么将什么都不会显示。
实体选择器:selector[编辑 | 编辑源代码]
selector会显示所有被选中的实体的名称。其值为一个目标选择器。
下面的对象将会显示所有苦力怕的名称:
{
"selector": "@e[type=creeper]"
}
效果:(假设已加载区块中只有一只苦力怕,且该苦力怕没有自定义名称)
苦力怕
如果选择到了多个实体,那么它默认会以“实体A, 实体B, 实体C, …”的形式列举。分隔实体的文本可以通过separator修改。
使用这种方法输出的文本可以通过⇧ Shift+左键获取对应实体的UUID。
按键键位:keybind[编辑 | 编辑源代码]
keybind用于显示对应功能的按键,其可以因玩家的设定偏好而改变。
下面的对象将会显示代表打开物品栏的键位:
{
"keybind": "key.inventory"
}
效果:
E
但如果更改了键位,那么它会显示为改变后的打开物品栏的键位。
游戏内分类 | 可用值 | 游戏内描述 | 默认键位 |
---|---|---|---|
移动 | key.forward | 向前移动 | W |
key.left | 向左移动 | A | |
key.right | 向右移动 | D | |
key.back | 向后移动 | S | |
key.sneak | 潜行 | 左⇧ Shift | |
key.sprint | 疾跑 | 左ctrl | |
key.jump | 跳跃 | space | |
游戏内容 | key.use | 使用物品/放置方块 | 鼠标右键 |
key.attack | 攻击/摧毁 | 鼠标左键 | |
key.pickItem | 选取方块 | 鼠标中键 | |
物品栏 | key.drop | 丢弃所选物品 | Q |
key.swapHands | 交换手中的物品 | F | |
key.inventory | 开启/关闭物品栏 | E | |
key.hotbar.[1-9] | 快捷栏[1-9] | 1 - 9 | |
创造模式 | key.saveToolbarActivator | 保存物品工具栏 | C |
key.loadToolbarActivator | 加载物品工具栏 | X | |
多人游戏 | key.chat | 打开聊天栏 | T |
key.playerlist | 玩家列表 | Tab ↹ | |
key.command | 输入命令 | / | |
杂项 | key.fullscreen | 全屏显示切换 | F11 |
key.smoothCamera | 切换电影视角 | ||
key.togglePerspective | 切换视角 | F5 | |
key.screenshot | 截图 | F2 | |
key.advancements | 进度 | L | |
key.spectatorOutlines | 高亮玩家(旁观者) |
本地化键名:translate[编辑 | 编辑源代码]
translate的值为一个本地化键名,用于在不同的语言下显示不同文本。为了适应游戏文本的需求,本地化键名对应的文本也支持插入译文变量,在输出时使用合适的文本替换。
translate大体上有两种用途:
- 显示本地化文本。
- 建立一个文本模板,再将合适的值替换进去。
显示本地化文本[编辑 | 编辑源代码]
下面的对象使用了石头的本地化键名,将会显示石头的名称:
{
"translate": "block.minecraft.stone"
}
在中文里,它会显示为石头
,但如果将游戏语言切换为英语,这个对象又会显示为Stone
。游戏里大部分需要显示文本的地方都使用了本地化键名,以支持多语言。
如果本地化键名在所选语言里没有对应值,它将使用en_us.json里该本地化键名的值。如果该本地化键名无法在任何语言文件里被找到,且没有备用译文,游戏会直接输出本地化键名。
下面我们再来看带有参数的本地化键名。multiplayer.player.joined
就是一个例子,它在中文语言文件里的值为%s加入了游戏
。其中%s
是一个参数。当有玩家加入游戏时,游戏会将玩家名称填入%s
的位置并输出。
现在,我们来尝试输出:
{
"translate": "multiplayer.player.joined"
}
结果为:
玩家加入了游戏
显然,%s
被直接输出了,这是因为我们还没有向参数赋值。这时,就必须用到with。
为其中的参数赋值:with[编辑 | 编辑源代码]
有些本地化键名会使用像“%s”和“%n$s”这样的参数来表示变量。直接使用translate时,这些参数会被直接输出。想要让参数也显示为特定的文本,就需要使用元素with。
with是一个列表,列表中的每一个元素都应该是一个文本组件对象。列表内的文本组件将会这样分配给每个参数:
- 对于“%s”而言,第一个“%s”会使用列表中的第一个文本组件,而下一个“%s”将会接着使用第二个文本组件,依次类推,直至不再出现“%s”。
- 对于“%n$s”而言,其将会直接使用列表中的第n个文本组件。
我们用一个例子来说明这一切。
translation.test.complex
是用于测试的本地化键名,其在zh_cn.json里的原始值如下:
前缀,%s%2$s 然后是 %s 和 %1$s 最后是 %s 还有 %1$s!
如果只使用translate元素,它看起来是这样的:
前缀,%s%2$s 然后是 %s 和 %1$s 最后是 %s 还有 %1$s!
现在,我们用with来给这些参数赋值:
{
"translate": "translation.test.complex",
"with": [
"a",
{ "text": "b" },
{ "text": "c", "color": "dark_red" }
]
}
它看起来是这个样子的:
前缀,ab 然后是 b 和 a 最后是 c 还有 a!
原始值中的“%1$s”和“%2$s”都分别对应了第1个元素"a"
和第2个元素{"text":"b"}
,而其中的“%s”按照从前往后的顺序分别被赋予了"a"
、{"text":"b"}
和{"text":"c","color":"dark_red"}
。
自定义输出文本[编辑 | 编辑源代码]
因为本地化键名可以使用资源包来自定义,所以你可以使用translate来自定义输出文本。
你可以使用资源包添加自定义本地化键名,以创建一个文本模板。例如,将本地化键名custom.text_template
指定为“第一个物品是%s,第二个物品是%s”。
要使用这个文本模板,只需在文本组件的translate中填写相应的自定义本地化键名,然后在with中填入需要传递的值:
{
"translate": "custom.text_template",
"with": [
{ "translate": "block.minecraft.grass_block" },
{ "translate": "block.minecraft.sand" }
]
}
效果:
第一个物品是草方块,第二个物品是沙子
备用译文:fallback[编辑 | 编辑源代码]
前文提到,如果本地化键名无法在任何语言文件里被找到,游戏会直接输出本地化键名。
本地化键名可以包含译文变量,不会影响with的使用,所以直接将本地化键名指定为可阅读的文本是可行的,例如:
{
"translate": "This is a %s",
"with": [
{ "translate": "Doctor" }
]
}
因为这些本地化键名通常并不存在,所以它会被直接输出;又因为本地化键名本身支持译名变量,所以“%s”会被正常替换。它的输出为:
This is a Doctor
但这么写并不规范,而不规范的写法会埋下翻译隐患。如果你使用文本组件是为了自定义地图,你就是在给译者下绊子。拿刚才的文本组件说明:
- 你是一个自定义地图译者。你想将这段文本翻译成中文,所以你需要指定本地化键名
This is a %s
和Doctor
的值。 - 将
This is a %s
指定为“这是一个%s”。 - 将
Doctor
指定为“医生”。 - 你发现作者又使用
Doctor
指代了他设计出的凋灵博士Boss,此处使用“医生”并不妥当。但你无法将Doctor
同时指定为“医生”和“博士”,只能选择保留原文/修改作者的命令等要么得罪玩家要么得罪作者的方式处理文本。
如果你使用translate不是为了支持本地化,并且你不在意文本组件的规范,你可以忽略这个问题并使用该方法。
规范的写法是使用fallback指定找不到本地化键名时的输出结果:
{
"translate": "custom.text",
"fallback": "This is a %s",
"with": [
{ "translate": "custom.text.doctor", "fallback":"Doctor" }
]
}
虽然麻烦了点,但翻译你的自定义地图的译者会感谢你的。
NBT标签的值:nbt[编辑 | 编辑源代码]
nbt用于获取方块、实体或命令存储的NBT标签的值,其以NBT路径的形式书写。
block、entity和storage用于指定要获取标签的方块、实体或命令存储。
block的值是一组方块坐标,以“<x> <y> <z>”的形式书写。其可以使用相对坐标,这时以生成文本的位置为基准点。
下面的对象将会显示执行者下面的方块的“Items”标签的值:
{
"nbt": "Items",
"block": "~ ~-1 ~"
}
entity的值是目标选择器。如果选择到了多个实体,那么获取的值将会依次列举。默认以,
分隔,但可以用separator修改。
下面的对象将会显示最近玩家的“Health”标签的值:
{
"nbt": "Health",
"entity": "@p"
}
storage的值是命名空间ID。
下面的对象将会显示存储test:a
中“a”标签的值:
{
"nbt": "a",
"storage": "test:a"
}
block、entity和storage只能使用其中的一个。若存在多个,则按以下优先级选择最终要使用的元素:
block > entity > storage
视为文本组件解析:interpret[编辑 | 编辑源代码]

interpret是一个布尔值。当其为true
时,由nbt获取的值将会视作文本组件解析并显示。
在147 78 48处有一个白桦木告示牌,其“front_text.messages[0]”的值如下:
'{"italic":true,"color":"dark_red","text":"I am a happy sign XD"}'
如果直接使用nbt获取标签的值,结果会直接输出为字符串,不会进行任何解析:
{"italic":true,"color":"dark_red","text":"I am a happy sign XD"}
但如果加入interpret元素并将其设为“true”:
{
"nbt": "front_text.messages[0]",
"block": "147 78 48",
"interpret": true
}
这会将获取到的值继续以文本组件解析,效果:
I am a happy sign XD
文本内容元素的优先级[编辑 | 编辑源代码]
一个对象里必须要有一个文本内容元素,但如果存在多个文本内容元素,其将会按照优先级决定最终要使用的文本内容元素。
文本内容元素的优先级如下:
text > translate > score > selector > keybind > nbt
如果一个对象里使用了多个相同的文本内容元素(例如连续使用了两次text),那么只有最后的元素会生效。
指定文本内容类型:type[编辑 | 编辑源代码]
使用type可以指定文本内容类型。此时将无视上文所提的优先级,强制使用type指定的文本内容元素。其可用值如下:
可用值 | 对应文本内容元素 |
---|
type并非必须项,在规范的文本组件中也不会影响输出的文本。但含有type元素的文本组件可提升解析与错误检查速度。
可交互文本[编辑 | 编辑源代码]
将特定文字填入聊天栏:insertion[编辑 | 编辑源代码]
insertion元素可以让玩家按住⇧ Shift点击文字时,将特定的一串文字输入进玩家的聊天栏中。输入的文本不会替换聊天栏里原有的文本。该元素只有在使用/tellraw
显示时才生效。
{
"text": "155652",
"insertion": "155652"
}
如果按住⇧ Shift点击文本,在玩家的聊天栏内将会输入“155652”。
点击事件:clickEvent[编辑 | 编辑源代码]
clickEvent可以用于指定在点击文本时产生的事件。该元素是一个JSON对象。
对象clickEvent内必须要有参数action和value。action是点击文本后执行的事件。value是要执行的事件的值。
action接受以下值:
action的值 | 描述 | value的值 | 可用性 |
---|---|---|---|
open_url |
打开网页 | 网页地址(http://example.com) | 聊天、成书 |
run_command |
发送命令 | 字符串(必须以“/”开头) | 聊天、成书、告示牌 |
change_page |
切换页码 | 正整数 | 成书 |
suggest_command |
输入命令 | 字符串 | 聊天 |
copy_to_clipboard |
复制内容至剪贴板 | 字符串 | 聊天、成书 |
悬停事件:hoverEvent[编辑 | 编辑源代码]
hoverEvent可以用于指定悬浮在文字上时产生的提示文本。该元素是一个JSON对象。
对象hoverEvent内必须要有参数action和contents。action是提示文本的类型,contents是提示文本的内容。
action接受以下值:
action的值 | 描述 | contents的值 | 备注 |
---|---|---|---|
show_text |
显示文字 | 文本组件 | contents可以是字符串、对象或列表。 |
show_item |
显示物品 | "<命名空间ID>" 或{"id":"<命名空间ID>","count":<数量>,"components":<物品堆叠组件>}
|
contents为对象,内容不需转义,其中count和components可选。contents也可以是字符串,作为id的值,例如"minecraft:stone" 和{"id":"minecraft:stone"} 等价。
|
show_entity |
显示实体 | {"type":"<实体类型ID>","id":<UUID>,"name":<实体显示名称>}
|
contents为对象,内容不需转义,其中name可选,为另一文本组件。 |
分隔文本中的多个项目[编辑 | 编辑源代码]
指定分隔符:separator[编辑 | 编辑源代码]
输出多个文本时,游戏会使用,
作为分隔符分隔不同文本。例如:
{
"selector": "@e[type=marker]"
}
效果:
标记, 标记, 标记, ...
separator用于自定义分隔符,将,
替换为不同内容。
例如,使用、
作为分隔符:
{
"selector": "@e[type=marker]",
"separator": {
"text": "、"
}
}
效果:
标记、标记、标记、...
文本组件对象间的关系[编辑 | 编辑源代码]
你可以将多个文本组件对象放入列表中。列表中的每个元素将会依次显示。但是需要注意的是,列表中的第2、3、4……个元素会继承第1个元素的样式(如有),但第2、3、4个元素的样式不被继承。
下面这个例子拥有2个对象:
[
{
"text": "Welcome to ",
"color": "dark_red"
},
{
"text": "Minecraft!",
"color": "blue"
}
]
效果:
Welcome to Minecraft!
父对象与子对象[编辑 | 编辑源代码]
在文本组件对象列表中,第一个对象的样式将会影响所有后续的对象。事实上,其余对象都是第一个对象的附属(siblings)。
下面这个例子中,因为子对象继承了父对象的样式,所以整个文本都会采用深红色+下划线的样式:
[
{
"text": "Welcome to ",
"color": "dark_red",
"underlined": true
},
{
"text": "Minecraft"
},
"!"
]
效果:
Welcome to Minecraft!
我们可以让第一个元素为空值,这样父对象就会没有样式,因此也不会有任何样式影响后续的文本:
[
"",
{
"text": "Welcome to ",
"color": "dark_red",
"underlined": true
},
{
"text": "Minecraft"
},
"!"
]
效果:
Welcome to Minecraft!
继承对象的属性:extra[编辑 | 编辑源代码]
extra内的文本组件将会继承该元素的所有属性。
{
"text": "Welcome to ",
"color": "dark_red",
"underlined": true,
"hoverEvent": {
"action": "show_text",
"value": {
"text": "Minecraft!"
}
},
"extra": [
{
"text": "Minecraft!"
}
]
}
效果:
Welcome to Minecraft!
可以看到,extra内的{"text":"Minecraft!"}
继承了原对象的color、underlined和hoverEvent元素。
需要注意的是,使用列表与使用"extra"
属性的对象是等价的,也就是说,下面两种JSON等价:
[{"text": "1"}, "2", "3", "4"]
{
"text": "1",
"extra": ["2", "3", "4"]
}
屏幕标题[编辑 | 编辑源代码]
/title
命令可输出聊天消息。

title @s title {"text":"第一章","bold":true}
title @s subtitle {"text":"故事的开始…","color":"gray","italic":true}
subtitle
只有在title
出现后才会显示。
你也可以使用纯文本形式。
title @s title "\u00a7l第一章"
title @s subtitle "\u00a77\u00a7o故事的开始…"
动画化标题[编辑 | 编辑源代码]
在一些小游戏服务器或相关地图场景中,会设计一些有动画感的标题。比较原始的做法是,使用多个命令方块,配合中继器延时,分时执行title
命令。

命令方块中的命令依次为:
title @p times 0 3s 0
title @p title {"text":"欢"}
title @p title {"text":"欢迎"}
title @p title {"text":"欢迎来"}
title @p title {"text":"欢迎来到"}
title @p subtitle {"text":"Mi"}
title @p subtitle {"text":"Mine"}
title @p subtitle {"text":"Minecr"}
title @p subtitle {"text":"Minecraf"}
title @p subtitle {"text":"Minecraft "}
title @p subtitle {"text":"Minecraft Wi"}
title @p subtitle {"text":"Minecraft Wiki"}
title @p subtitle {"text":"Minecraft Wiki!"}
为了更好地控制间隔时间和播放内容,可以考虑使用函数来生成帧并播放帧:

generic:text_component/animated_title/start
scoreboard objectives add var dummy
# 一共多少帧
scoreboard players set frame_num var 0
# 帧计数
scoreboard players set frame_cnt var 0
# 每帧长度为3s,无谈入淡出
title @p times 0 3s 0
# 输入清空
data remove storage test:animated_title input
# 初始化输入的文本
data modify storage test:animated_title input.text set value "欢迎来到"
# 输出清空
data remove storage test:animated_title output
# 初始化输出的帧序列
data modify storage test:animated_title output.frames set value []
# 设置帧数
execute store result score frame_num var run data get storage test:animated_title input.text
# 设置帧计数器
scoreboard players operation frame_cnt var = frame_num var
# 根据帧数生成相应数量的帧
function generic:text_component/animated_title/gen
# 设置帧计数器
scoreboard players operation frame_cnt var = frame_num var
# 根据帧数播放相应数量的帧
function generic:text_component/animated_title/play

generic:text_component/animated_title/gen
execute if score frame_cnt var matches ..0 run return fail
data modify storage test:animated_title output.frames append from storage test:animated_title input.text
data modify storage test:animated_title input.text set string storage test:animated_title input.text 0 -1
scoreboard players remove frame_cnt var 1
function generic:text_component/animated_title/gen

generic:text_component/animated_title/play
# 输出该帧
title @p title {"nbt":"output.frames[-1]", "storage":"test:animated_title", "interpret": true}
# 帧减一
data remove storage test:animated_title output.frames[-1]
scoreboard players remove frame_cnt var 1
# 帧播放控制
# 帧计数为0时停止
execute if score frame_cnt var matches ..0 run return fail
# 间隔设置为0.5秒
schedule function generic:text_component/animated_title/play 0.5s
聊天消息[编辑 | 编辑源代码]
/tellraw
命令可输出聊天消息。
你可以更改聊天栏消息的颜色和样式。

tellraw @s {"text":"Hello world!", "bold":true, "color":"light_purple"}
tellraw @s {"text":"oh yeah hi", "strikethrough":true, "color":"light_purple"}
tellraw @s {"text":"Hai", "underline":true, "color":"light_purple"}
tellraw @s {"text":"hiiiii", "italic":true, "color":"light_purple"}
tellraw @s {"text":"Hi"}
悬停事件[编辑 | 编辑源代码]
hoverEvent的show_item
悬停事件允许你指向一段文字后悬浮显示一个物品提示框。
/tellraw @s [{"text":"青青陵上柏,磊磊涧中"}, {"text":"石", "hoverEvent": {"action": "show_item", "contents":{"id":"stone", "count":"32"}}}]
- 这里的“石”字会悬浮显示“石头”的物品信息。
count
字段对显示效果无影响。
在此基础上,修改文本内容,添加一些组件:
tellraw @p [{"selector": "@p"}, {"text":"获得了一把"}, {"text":"[绝魂剑]", "hoverEvent": {"action": "show_item", "contents":{"id":"diamond_sword", "count":"32", "components":{"minecraft:custom_name": "{\"text\":\"绝魂剑\",\"italic\":false}", "enchantments": {"sharpness":3,"knockback":2}}}}}]
以上命令的确可以展示一个物品,但有一个限制:所展示物品的信息由函数文件直接定义,除非修改函数文件,否则物品信息不可变。为使所显示的物品信息是动态的,考虑使用宏函数。
点击事件[编辑 | 编辑源代码]
点击事件一般用于模拟“文本按钮”。
例如,在聊天栏执行以下命令:
/tellraw @s {"text":"[点我上升1格]", "clickEvent":{"action":"run_command","value":"/tp ~ ~1 ~"}}
点击聊天栏所显示的“[点我上升1格]”文本,你将会被向上传送1格。但是要注意,当玩家点击带有点击事件的文本尝试执行相应命令,将先检查该玩家的权限是否满足该命令的所需权限,否则将无法执行。所以,在多人游戏中,你还需用/trigger
命令作为信号媒介,间接检测玩家的点击情况。
例如,首先创建一个signal0
记分板,以表示该记分板是“事件0的信号”:
scoreboard objectives add signal0 trigger
我们希望让玩家点击文本后,让“事件0以信号1触发”,故有如下命令:
/tellraw @s {"text":"[点我上升1格]", "clickEvent":{"action":"run_command","value":"/trigger signal0 set 1"}}
当然,要确保玩家拥有trigger
命令的触发权限,故每次都应预先执行:
scoreboard players enable @a signal0
这样,玩家便能够通过点击文本来改变其signal0
值,之后只需要监测该信号值即可知道玩家的点击情况:
execute if score @s signal0 matches 1 run function test:click_event0
一般地,可以将这里的“事件0”看做是一个“布尔事件”,也即,当其信号值为1时代表事件发生,为其他值时代表事件不发生。当然,你也可以定义多个带有不同意义的信号值,这些信号值可以看做“事件0”的一系列“参数”。例如,以下命令即代表“使用不同参数触发事件0”:
execute if score @s signal0 matches ..0 run return run function test:click_event0_with_lower_bound
execute if score @s signal0 matches 1 run return run function test:click_event0_with_1
execute if score @s signal0 matches 2 run return run function test:click_event0_with_2
execute if score @s signal0 matches 3 run return run function test:click_event0_with_3
execute if score @s signal0 matches 4.. run return run function test:click_event0_with_upper_bound
当然,你也可以使用宏函数,以将信号值直接传入事件中:
# 事先移除input标签:
data remove storage test:test input
# 将信号值存入input标签,作为输入参数:
execute store result storage test:test input.args.signal int 1 run scoreboard players get @s signal0
# 传递input标签中的1个参数,该参数也即事件0的信号值:
function test:click_event0 with storage test:test input.args
动作栏文本[编辑 | 编辑源代码]
在各种Minecraft地图中,动作栏常用于显示地图自定义的玩家数值信息。比如:

具体实现如下:
# 创建红宝石记分项
scoreboard objectives add ruby dummy
# 创建魔力记分项
scoreboard objectives add magic dummy
# 定义actionbar数据结构
data merge storage test:actionbar {show: [{text: "", color: "aqua", extra: [{text: "困倦"}, {text: "状态:"}]}, {text: "", color: "light_purple", extra: [{text: "魔力:"}, {score: {name: "@s", objective: "magic"}}]}, {text: "", color: "red", extra: [{text: "红宝石:"}, {score: {name: "@s", objective: "ruby"}}]}]}
# 根据读取actionbar数据结构的nbt数据,解析为文本组件并显示(每部分间用一个空格隔开)
execute as @p run title @s actionbar {"nbt":"show[]", "storage":"test:actionbar","interpret":true, "separator": {"text":" "}}
- 为了更方便管理动作栏的文本内容,这里将动作栏分为三部分,每一部分又有两个子部分。三部分即show列表中三个元素,每个元素中带有一个extra列表存储该部分被划分为的所有子部分。这样,NBT路径
show[i].extra[j]
就代表动作栏从左到右数第i+1
个部分的第j+1
个子部分。比如,“魔力”二字可通过show[1].extra[0]
来访问。 - 还需注意,
/title @s actionbar
命令后,NBT路径必须写为show[]
。因为show[]
代表show列表中的所有元素,而非show列表本身。之后,separator
才会在这些元素之间添加自定义的分隔符,最终组合为一个完整的文本组件。若写为show
,则会直接将整体视作单个文本组件,无法添加分隔符。
动画化动作栏[编辑 | 编辑源代码]
- 闪烁效果
由于之前已经使用storage
来构造了一个NBT数据结构,故可直接分时改变该结构中的数据。

test:actionbar
命令存储中的数据,以模拟动画效果显示心形:
data modify storage test:actionbar show[0].extra[1] set value {"text":"❤","color":"red"}
show[0].extra[1]
中的show[0]
表示动作栏第一部分“状态:❤”,extra[1]
表示第一部分的第二个子部分,也即表示具体状态的文本“❤”。
过一段时间后,再让心形消失:
data modify storage test:actionbar show[0].extra[1] set value {"text":" ","font":"uniform"}
- 这里使用
"font":"uniform"
来给两个空格设置字体,是为了保证整个动作栏的长度无变化——Uniform字体与Minecraft默认字体所占的空间不同。
以上两条命令交替执行即可实现闪烁心形动画效果。
为了让动作栏持续显示,你需要高频执行:
execute as @p run title @s actionbar {"nbt":"show[]", "storage":"test:actionbar","interpret":true, "separator": {"text":" "}}
如果你没有通过/title times
将淡入淡出时间设置为0,也不会影响动作栏的视觉效果——它不会像标题文本那样闪烁。
- 数值条
上例中的“魔力”值不仅可以用传统的数值方式显示,还可以显示为数值条。由于使用文本组件来实现,而文本组件的基本构成要素就是一个一个字符,故数值条的基本实现思路就是用多个字符和不同颜色来模拟可变的条形。

以下示例展示了一个以“■”字符构成的数值条:
max_bar_len
| |
cur_bar_len |
padding_bar_len
|
■■■■■ | ■■■■■■■■■■ |
相关变量如下:
max_bar_len
:条的最大长度。cur_bar_len
:条当前被染色的长度。padding_bar_len
:条剩余的“空白”长度。
为了能让数值条和实际的数值有较好的兼容性,故另设置一系列和数值有关的变量:
max_magic
:最大魔力值。cur_margic
:当前魔力值。
编写如下函数,以正确将魔力值转换为魔力条长度:

generic:text_component/actionbar/number_to_bar_len
# 计算当前百分比值 percent = cur_magic*100 / max_magic
scoreboard players operation #temp var = cur_magic var
scoreboard players operation #temp var *= #100 var
scoreboard players operation #temp var /= max_magic var
# 利用百分比计算条长度 cur_bar_len = percent * max_bar_len / 100
scoreboard players operation #temp var *= max_bar_len var
scoreboard players operation #temp var /= #100 var
scoreboard players operation cur_bar_len var = #temp var
# 计算填充长度
scoreboard players operation #temp var = max_bar_len var
scoreboard players operation #temp var -= cur_bar_len var
scoreboard players operation padding_bar_len var = #temp var
魔力条长度应根据魔力值实时更新,然后更新到test:actionbar
数据结构中,并使用/title actionbar
来显示:

generic:text_component/actionbar/update_bar/0
function generic:text_component/actionbar/number_to_bar_len
execute store result storage test:actionbar input.cur_bar_len int 1 run scoreboard players get cur_bar_len var
execute store result storage test:actionbar input.padding_bar_len int 1 run scoreboard players get padding_bar_len var
function generic:text_component/actionbar/update_bar/1 with storage test:actionbar input

generic:text_component/actionbar/update_bar/1
$data modify storage test:actionbar show[1].extra[1].text set from storage test:actionbar magic.bar.slices[$(cur_bar_len)]
$data modify storage test:actionbar show[1].extra[2].text set from storage test:actionbar magic.bar.slices[$(padding_bar_len)]
- 动作栏的显示文字中,“魔力”后的魔力条由两部分组成:染色部分、未染色部分。染色部分用来表示条的已占用量,未染色部分用来表示条还未被占用的空余量。
test:actionbar show[1].extra[1].text
表示动作栏中,从左到右第2个显示位的extra中的第2个元素中的文本。该文本即条上被染色的部分。test:actionbar show[1].extra[2].text
表示动作栏中,从左到右第2个显示位的extra中的第3个元素中的文本。该文本即条上未被染色的部分。
我们期望条的染色和未染色部分均可随魔力值动态变化,故这里使用宏函数,动态引用来自test:actionbar
数据结构中的各分片magic.bar.slices
。magic.bar.slices
应事先根据条最大长度预处理出:

generic:text_component/actionbar/slice_bar
execute if score #temp var matches ..-1 run return fail
data modify storage test:actionbar magic.bar.slices prepend from storage test:actionbar magic.bar.display_buf
data modify storage test:actionbar magic.bar.display_buf set string storage test:actionbar magic.bar.display_buf 0 -1
scoreboard players remove #temp var 1
function generic:text_component/actionbar/slice_bar
- 分片的核心原理即使用
data ... set string
来获取字符串子串,并用子串更新当前串。 magic.bar.display_buf
缓冲用来临时存放每个分片。#temp
是一个临时寄存量,它应在执行前加载max_bar_len
的分数。- 之所以迭代到-1才停止,是为了让空字符串
""
也被添加到分片列表中。这样即可使magic.bar.slices[0]
得到空串。
到此,逻辑处理部分已比较完整。故可编写函数实现初始化部分,以设定显示外观和各变量的初值:

generic:text_component/actionbar/init
data remove storage test:actionbar show
# 要显示的文本组件结构
data modify storage test:actionbar show set value [ \
{"text":"", "color": "aqua", extra: [{"text":"状态:"}, {"text": "困倦"}]}, \
{"text":"", "color": "light_purple", extra: [{"text":"魔力:"}, {"text": "", "color": "light_purple"}, {"text": "", "color": "gray"}]}, \
{"text":"", "color": "red", "extra": [{"text":"红宝石:"}, {"score": {"objective":"ruby", "name":"@s"}}]} \
]
data remove storage test:actionbar magic
# 魔力条外观
data modify storage test:actionbar magic.bar.display set value '||||||||||||||||||||'
# 魔力条外观缓冲
data modify storage test:actionbar magic.bar.display_buf set from storage test:actionbar magic.bar.display
# 魔力条分片列表,下标为0的元素代表长度为0的魔力条分片
data modify storage test:actionbar magic.bar.slices set value []
# 最大魔力和当前魔力
scoreboard players set max_magic var 100
scoreboard players set cur_magic var 0
# 魔力条最大长度和魔力条当前长度
execute store result score max_bar_len var run data get storage test:actionbar magic.bar.display
scoreboard players set cur_bar_len var 0
# 魔力条填充长度
execute store result score padding_bar_len var run scoreboard players get max_bar_len var
# 字面量100
scoreboard players set #100 var 100
# 对魔力条进行分片
scoreboard players operation #temp var = max_bar_len var
function generic:text_component/actionbar/slice_bar
然后编写测试函数,高频调用:

test:test
# run on tick
function generic:text_component/actionbar/update_bar/0
execute as @p run title @s actionbar {"nbt":"show[]", "storage":"test:actionbar","interpret":true, "separator": {"text":" "}}
若以上步骤均正确,则此时应该能看到“空的魔力条”显示在动作栏,这表示当前的魔力值为0。执行如下命令,设置当前魔力值为20:
scoreboard players set cur_magic var 20
即可看到魔力条根据设定的魔力值进行了染色。
告示牌[编辑 | 编辑源代码]
告示牌有正面文本front_text和背面文本back_text,均可显示不同的文本内容。
右图告示牌的主要SNBT如下:
{ front_text: { has_glowing_text: 0b, color: "black", messages: [ '"JSON文本"', '"可以设置"', '{"text": "文字的","extra":[{"text":"颜色","color":"dark_red"}]}', '""' ] }, <...> } |
![]() |
可见,messages的4个元素依次控制告示牌的4行。每个元素都是一个字符串,且字符串必须为JSON格式的文本组件。对于第1、2行,字符串内容为"JSON文本"
和"可以设置"
,这表示两个JSON字符串形式的文本组件。对于第3行,字符串内容为{"text": "文字的","extra":[{"text":"颜色","color":"dark_red"}]}
,这表示一个JSON对象形式的文本组件,其带有子组件{"text":"颜色","color":"dark_red"}
。
在任意告示牌处执行如下命令即可观察实际效果:
data merge block ~ ~ ~ {front_text: {color:"black", messages: ['"JSON文本"', '"可以设置"', '{"text":"文字的", "extra":[{"color":"dark_red","text":"颜色"}]}', '""']}}
如果你要单独修改某一个元素,必须保证元素类型为字符串,否则无法修改成功。在以下命令中,hello
虽然没有包围引号,但仍可以被默认解析为字符串型标签值。
/data modify block ~ ~ ~ front_text.messages[0] set value hello
那么将以上命令后部分修改为set value 123
是否也行?在命令解析阶段,123
被默认解析为整型,而front_text.messages[0]
显然需要字符串型,所以不行。那修改为"123"
呢?答案依然时不行。在命令解析阶段,"123"
被解析为字符串型,与front_text.messages[0]
的类型匹配,但是其具体内容的编码规则无法对应。在书写命令时,set value "123"
中的两个引号作为语法定界符并不会真正被传递。实际解析出来的字符串内容仅包含123
这3个字符。而单独的123
无法被视作一个文本组件——它将被解析为一个数值,而非字符串、对象、列表等合法的文本组件形式。所以正确写法应为:
/data modify block ~ ~ ~ front_text.messages[0] set value '"123"'
转义[编辑 | 编辑源代码]
以下命令尝试使用\u00a7a
(即格式化代码§a
)来修改颜色,应写为\\u00a7a
。因为\
在SNBT中的意义为“转义符”。要使字符串值中真正存储一个\
,应使用\\
。
data modify block ~ ~ ~ set value '"\\u00a7ahello"'
- 实际上,
front_text.messages[0]
中的数据为"\u00a7ahello"
。因为'
是字符串标识符,不是真正的字符串内容;"
在SNBT中本来也代表字符串标识符,但是由于其被包裹在一对单引号中,故被转义为一般意义下的双引号,作为字符串内容被存储;\\
被转义为\
,也作为字符串内容被存储。
上例的转义是SNBT中的转义。由于告示牌每一行的数据均为一个JSON格式的字符串,故也存在JSON转义。
例如,要实现告示牌输出以下文本:
"你好"
首先需要将其表示为一个JSON格式的文本组件:
{"text":"\"你好\""}
- 这里的
\"
将字符串标识符"
转义为一般意义下的双引号"
。
在书写SNBT格式时,又要进一步进行转义:
data modify block ~ ~ ~ set value '{"text": "\\"你好\\""}'
'{"text": "\\"你好\\""}'
涉及的转换过程如下:
'{"text": "\\"你好\\""}'
→NBT字符串,存储的数据为{"text": "\"你好\""}
{"text": "\"你好\""}
→JSON对象,包含一个JSON字符串,存储的数据为"你好"
点击事件[编辑 | 编辑源代码]
玩家右键告示牌的某一面时,会尝试触发告示牌该面包含clickEvent的文本组件。
如果告示牌存在多行带点击事件的文本,则依次执行每行命令。
对一个告示牌执行如下命令:
data modify block ~ ~ ~ front_text.messages[0] set value '{"text":"[Text0]", "clickEvent":{"action":"run_command","value":"say Text0"}}'
data modify block ~ ~ ~ front_text.messages[1] set value '{"text":"[Text1]", "clickEvent":{"action":"run_command","value":"say Text1"}}'
data modify block ~ ~ ~ front_text.messages[2] set value '{"text":"[Text2]"}'
data modify block ~ ~ ~ front_text.messages[3] set value '{"text":"[Text3]", "clickEvent":{"action":"run_command","value":"say Text3"}}'
若玩家点击该告示牌的正面,则从front_text.messages[0]
开始一直执行到front_text.messages[3]
,其中front_text.messages[2]
无点击事件,故直接跳过,但不影响后续命令的执行。聊天栏将依次显示say Test0
、say Test1
和say Test3
命令的执行结果。
由于点击事件是由玩家触发的,故这些命令的执行者为点击告示牌的玩家,除此之外,这些命令都默认以告示牌的方向、位置执行,详见命令上下文 § 告示牌。与函数类似,相邻两行文本之间的上下文也不会继承或叠加。假设告示牌中有如下2行命令:
tp @s ~ ~10 ~ ~ ~
tp @s ~ ~10 ~ ~ ~
这两条命令将被依次执行,其中@s
代表点击告示牌的玩家,~ ~10 ~
代表告示牌的方块中心向Y轴正方向移动10格的位置。之后的~ ~
为告示牌命令上下文中的执行朝向,为(0, 0)
。故玩家点击仅包含该2条命令的告示牌后,会传送到距离告示牌上方约10格(而非20格)的位置,并且传送的一瞬间,视角会被强制设置为(0, 0)
。
参见[编辑 | 编辑源代码]
导航[编辑 | 编辑源代码]
[隐藏] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|