下面,就让我们来探讨一下,如何加入NBT。
2. NBT
NBT(二进制命名标签,
N
amed
B
inary
T
ags)
是Minecraft中用于向文件中存储数据的一种存储格式。
NBT (Named Binary Tag) is a tag based binary format designed to carry large amounts of binary data with smaller amounts of additional data.
NBT是一种基于二进制的存储格式,可以用少量的代码来存储大量的信息。
——Notch
上面的是Notch对于NBT的做出的规范。
然而——
跟我们一点关系都没有。
实际上,上面说的“NBT”是一种经过编译后的,人类完全看不懂的语言。
而我们今天要说的,是这种语言的“人类能看懂的格式”。
这种格式叫做
SNBT(字符串化的二进制命名标签,
S
tringified
NBT
)
本格式与上面的“.nbt”表达的是一种东西,只是用的是不同的两种语言罢了——一种给人读,一种给机器读。
——但是你也不用担心什么时候是不是该叫“SNBT”,什么时候又该叫“NBT”——
在命令相关的语境中,“NBT”总是在指其SNBT的格式。
2.1 标签,与键值对
那么,回到正题上来。
NBT长啥样呢?它规定了什么?又如何加入到命令中呢?
我在本系列教程的第1帖提到过一个有关奶的例子。
这个例子说的是,如何精确地描述一杯奶。
我们在这里,可以使用像下方所示的一样,用多个项目与值来描述这杯奶:
-
奶的种类
:
牛奶
-
奶的量
:
400ml
-
装奶的容器是否密封
:
否
-
奶的产源
:
卖奶的老奶奶
-
离过期还有
:
约3天
这样一来,你应该对我桌子上放的这杯奶有点了解了。
NBT差不多就是一样的东西:
让我们简单地描述某一只僵尸:
-
当前生命值
:
16.8滴血
#
-
是不是小僵尸:
是
-
是否可以破坏门:
否
-
剩余着火的时长:
5秒(100游戏刻)
-
是否无敌:
是
-
转变成溺尸之前的剩余时间
*
:
10秒(200游戏刻)
-
头部的装备:
钻石头盔
-
腿部的装备:……
-
面向的方向:……
注释:
#此处的生命值为当前生命值,而非生命值上限。注意虽然不会显示,但是生命值是有小数的。
*当僵尸在水中时,会在一定时间后转变为溺尸。
由于可以说的东西太多,我只列了一些。但各位可以从上面的描述中,想象出一只确定的僵尸了吧。
NBT说的大概就是这样的东西。
上面的每一个
“
描述
:
值
”
,都被称为“
键值对(Key-Value Pair)
”
这是个挺起来好像很强的名字,但实际上说的只是——
冒号
前面的“
描述
”被称为
“键”
,也叫
“标签名”
——通俗地说就是“
描述了本项目在
讲什么
。
”
冒号
后面的东西就是
“
值
”
——通俗地说就是
“对于这个
描述
(或者说键
或
标签名
),我们要它是
多少
”
因为
“键”与
“值”
总是
成对出现
,所以我们叫每一对为
“键值对”
。
相信你理解了吧。
那接下来的事情非常简单,我们只需要把上面的描述换成
NBT所接受的特定的英文
的就好了。
方便起见,让我们把上面的列出的9个键值对简化为以下4个:
-
Health
:
16.8 (当前生命值16.8滴血)
-
IsBaby:
1 (是小僵尸)
-
Fire:
100
(
剩余着火事件(刻):
100游戏刻
)
-
DrownedConversionTime:
200
(
转变成溺尸之前的剩余时间为
200游戏刻)
我们只是把上面的中文换成了特定的英文罢了。
——以上面的“
转变成溺尸之前的剩余时间
”为例——
-
DrownedConversionTime:200
复制代码
恭喜你,得到了第一个NBT。
这个标签描述了一个只剩10秒(200刻)就会变成溺尸的僵尸
【注2】
。
那么,举一反三,看一下剩下的3个吧。
生命值
“
Health
”是
16.8
滴血。
燃烧时间
“
Fire
”是
100
游戏刻(20秒)。
至于最后的是否是小僵尸“
IsBaby
”,对于这种“要么是要么不是”的真或假二选一问题。我们
用0代表否,1代表是
。
所以,
是否是小僵尸
“
IsBaby
”为
1
(是小僵尸)。
所以说,我们很轻松地就得到了以下三项:
Health
:
16.8
Fire
:
100
IsBaby
:
1
所以上面的三对标签分别描述了“生命值为16.8,剩余燃烧时间100刻”,与“是小僵尸”喽?
不不不,不是。我们不这样写。
我们要加上一些东西:
Health
:
16.8
f
Fire
:
100
s
IsBaby
:
1
b
啊?
什么鬼。
哪三个字母“
f
”,“
b
”,“
s
”是什什么意思?
为什么要在特定的标签后添加这3个字母?为什么前面的“
DrownedConversionTime:200
”后面就什么都不用添加?
【3】
这些字母是“
数据类型(Data Type)
”。
2.2 数据类型
什么是加在数据后面的字母?
这种加在数字后面的字母你应该不陌生。
比如——
400
L
——
400升
。
我的意思是,那些数字后的字母,有点像是这个数字的“单位”。
当然,这完全不是一种东西。
它们是“
数据类型
”。
什么是数据类型?
容我这么解释:
电脑存储数据是需要空间的,而不同的数据需要的“
空间
”的
大小
或
类型
有所不同。
比如说,“
是否是小僵尸
”这个标签只有两种可能——
要么是
,
要么不是
。我们只需要一个很小的空间就能把它存进去——因为只有两种可能,0或1。
但,像是“
着火时间
”这个标签所需要的电脑空间就要大得多,因为我们要存储可能的大量不同的时间值,而不仅仅是“0或1”。
但由于“
着火时间
”这个标签的单位是游戏的最小时间单位“游戏刻”,所以,是不可能出现小数的。
这一点就和“
生命值
”不同。虽然我们无法在血条上看到小数的生命值,但是只要你对游戏有一定的理解就知道生命值是有小数的。
这里就又是一个不同之处。“
着火时间
”是不会有小数的,我们为它分配的存储空间也不需要担心小数的问题,可以节省很多空间。
但是“
生命值
”是有小数的,我们需要一个不同类型的空间来存储这个数值。
以上这多种不同的存储空间,就被称为“
数据类型(Data Type)
”
上方在数字后面的字母,就代表了,
该标签使用的是什么样的数据类型
。
我们共有7种不同的数据类型,用于存储不同种类,不同大小的数据
,均列在下表:
|
图标
|
英文
|
学名
|
白话
|
格式
|
储值范围
|
备注
|
|
Byte
|
字节型
|
只能存非常小的整数
|
<数字>
b
|
-128~127
|
上面的“
IsBaby
:
1
b
”后方的“
1
b
”就是本类型
|
|
Short
|
短整型
|
能存较小的整数
|
<数字>
s
|
-32768~32767
|
上面的“
Fire
:
100
s
”中的“
100
s
”就是本类型
|
|
Int
|
整型
|
存储整数
|
<数字>
不需要添加字母后缀
|
-2147483648~
2147483647
|
上面的“
DrownedConversionTime
:
200
”中的
200
就是本类型。注意这种类型没有字母后缀。
|
|
Long
|
长整型
|
存很大的整数
|
<数字>
l
|
-2
63
~2ˆ
63
-1
|
本类型与上面的Int一样用于存储整数,但是范围要大得多。
|
|
Float
|
单精度浮点型
|
小数
|
<数字>
f
|
根据数据精度而定,最大为3.4*10
38
|
上面的“
Health
:
16.8
f
”中的“
16.8
f
”就是本类型。
|
|
Double
|
双精度浮点型
|
范围更大的小数
|
<数字>
d
|
根据数据精度而定,最大为1.8*10
308
|
本类型与上面的Float一样用于存储小数,但是范围要大得多。
|
|
String
|
字符串
|
一串文字
|
"
字符
"
或
'
字符
'
单双引号均可。
|
65,535个字节(大部分中文占用3个字节)
|
存储一串文字。有时会使用JSON文本。
|
就这样。很简单。
生命值
是"
单精度浮点型
",或者说“
小数
”,所以我们要在后面加上“
f
”:
(先不要管该如何知道一个标签的数据类型是什么的问题,这个我们后面说。)
剩下的3个标签同理:
Health
:
16.8
f
Fire
:
100
s
IsBaby
:
1
b
DrownedConversionTime
:
200
恭喜!
你终于得到了真正的标签。
上面的4对标签正确地描述了一只僵尸的生命值,燃烧事件,小僵尸,以及转化溺尸的时间。
但这里注意,冒号“:”是英文的冒号,千万别打错成中文的。
还有,NBT是大小写敏感的,别看错大小写字母,但是数据类型的后缀是不需要区分的。IsBaby:1b与IsBaby:1B都可以。
接下来,我们要做的就是把它们安进命令里就好了:
让我们看向,拥有NBT时的/summon指令格式:
-
/summon <实体> [<x> <y> <z>] [<NBT>]
复制代码
指令格式标出了NBT应该被加入的位置——命令最后面。
欸欸额,别着急动手,我们还有一个问题——怎么添加NBT?
<NBT>是一个元素。但是我们要添加的标签足足有上面的4个。
总不能写
"/summon zombie ~ ~ ~ Health:16.8f Fire:100s IsBaby:1b DrownedConversionTime:200"
吧。
我们该如何将4个标签整合成一个元素?
要用到的东西是,
复合标签(Compound)
。
2.3 复合标签
所谓复合标签,实际上就是把多个键值对整合为一个元素。
听起来好像很高大上,但实际上非常简单:
就像我们有一堆文件,怎么打包处理呢?只要放进文件夹里就好了:
使用 “
{}
”作为开头与结尾,不同的标签间用“
,
”相隔
这玩意你只要看一下例子就知道了。
该如何把我们上面的4个标签组合为1个复合标签呢?
根据上面的说明,十分简单。
我们用大括弧”{}“代表开头与结尾:
{}
再把上面的4个标签装进去:
{
Health
:
16.8
f
Fire
:
100
s
IsBaby
:
1
b
DrownedConversionTime
:
200
}
最后用
英文的逗号”,“
将不同的标签隔开:
{
Health
:
16.8
f
,
Fire
:
100
s
,
IsBaby
:
1
b
,
DrownedConversionTime
:
200
}
完成。
你得到了一个真正的NBT!
(而不是刚才的”真正的标签“)
简单地把上面的东西按到命令中——
-
/summon zombie ~ ~ ~ {Health:16.8f, Fire:100s, IsBaby:1b, DrownedConversionTime:200}
复制代码
在原地召唤一只生命值16.8,会继续着5秒的火,并会在10秒后变成溺尸的小僵尸。
有一件要注意的事情是,在NBT这对”{}“中是无视空格的。所以我在每一个“
,
”后都加了一个空格,为了便于阅读。
还有,NBT的顺序是完全无所谓的,哪一个放前面都行。
就这样。
同样,我们可以生成一只无敌而且有手臂的盔甲架。
“
ShowArms
”是是否拥有可以持有东西的手臂,是字节型“
Byte
”。
“
Invulnerable
”是是否无敌。同样是字节型“
Byte
”。
所以,根据上面的例子举一反三——
-
/summon armor_stand ~ ~ ~ {Invulnerable:1b,ShowArms:1b}
复制代码
生成一个无敌且有手的盔甲架。
2.4 复合内的复合标签
你可能知道有一条命令叫做“/give”,其作用是给予玩家物品:
非常简单,“<目标>”应是一位在线玩家的ID或选择器。物品ID与在第9帖讲过的方块ID是一个东西,只要按下F3+H打开更详细的提示即可查看(详见第
系列第十帖
)
数量为该物品的数量,不输入默认为1个:
例:
给与所有玩家10颗石头。
给予Dahesor(我)1颗钻石。
很好理解,不多说。
各位,本指令也是可以添加NBT的。
但是你可能会发现,命令格式中并没有NBT的位置。
在本命令中,NBT应被添加到物品ID的后面,没有空格,因为这相当于是一个元素:
比如,弩有一个标签Byte类型的标签“Charged”用于表示弩有没有上弦。
根据1为“真”,0为“假”的规律,我们就很容易得到:
-
/give @a Dahesor minecraft:crossbow{Charged:1b} 1
复制代码
给予所有玩家一个上弦的弩(虽然射不出来,因为没有指定装填了什么。)
好,你应该理解了。
接下来,你可能知道,掉落物也是一种实体,其实体ID为“item”。
我们也是可以用上面说过的/summon指令在某处召唤一个掉落物的。
但这很奇怪,因为“item”只是物品的意思,并没有说“这是什么物品”。
这是因为,“
是什么物品
”这项信息被包含在了它的NBT"
Item
"中。这是一个
字符串
的数据类型。
比如,我们要生成一个掉落的物品。什么物品呢?就是上面那个上弦的弩。
那么,它写作
Item
:
"
crossbow
"
Item
为标签名“是什么物品”,而
crossbow
则是"弩"。
因为这是一个字符串。所以根据上面的表格,我们要把它用双引号引起来(单引号也可以)。
接下来,我们还可以在其中包含“
是否上弦
”这个信息,就像我们在/give指令中做的那样——
Charged
:
1
b
我们可以生成一个“掉落在地面上的,上弦的弩”。
同理,我们也可以包含“这一坨掉落物有几个”这个信息——
Count
:
2
b
其中,“
Count
”为标签名,即“数量”的意思。2b则是一个Byte类型的值,代表了有两个。
但现在问题来了。
“
什么物品
”是“
掉落的物品
”这个实体的NBT。
但是“
弩是否上弦
”又是“
物品
”的NBT的一部分。
我们该怎么办?
难道是写:
/summon item ~ ~ ~ {Item:"minecraft:crossbow", Charged:1b, Count:2b}
这样?
不不不,不是不是。
这样乱了主次了。
“
是否上弦
”,与“
数量
”是用来形容物品的,而不是掉落的物品这个实体的!这样直接写乱了主次。
这怎么办呢?
答案很简单——我们在这个大的复合标签里,再套一个复合标签:
{
Item
:
{
id
:
"
minecraft:crossbow
"
,
Count
:
2
b
}
}
最外侧的,被我用绿色,较大号字体标记出的,就是NBT复合标签的那对大括号。
里面的“
Item
:
”则是标签名。
其冒号后面的内容就是它的值。但是在这里我们无法简单地使用一个数字来表示它的值,
所以这里,值是又一个复合标签的大括号,就是紫色的那对。
其中包含了两个另外的两对键值对:
id
:
"
minecraft:crossbow
"
告诉我们“
这是一个弩
”。id就是“物品ID”的意思。
Count
:
2
b
告诉我们,“
弩有两个
”。
这样就对了。
id
与
Count
分别是两个不同的标签,但是两者整个又是
Item
的值,也叫
子标签
。
你可想象成,在文件夹里夹另一个文件夹。
所以:
-
/summon item ~ ~ ~ {Item:{id:"minecraft:crossbow", Count:2b}}
复制代码
召唤一个掉落物,为两个弩。
欸欸,等等,我们不是说好了要包含“
是否上弦
”这个信息的么?
那,各位,怎么加入这个NBT?
这样?:
{
Item
:
{
id
:
"
minecraft:crossbow
"
,
Count
:
2
b
,
Charged
:
1
b
}
}
哦不不不不。这样主次又乱了
。
我们的“
是否上弦
”是用来形容“弩”的!
如果像上面那样写,就变成是
形容物品
的啦。
仔细看看就能明白,以下三个信息根本就不是并列的:
那怎么办呢?
简单,我们在这个被套进
复合标签
的
复合标签
中,再套一个
复合标签
:
{
Item
:
{
id
:
"
minecraft:crossbow
"
,
Count
:
2
b
,
tag:
{
Charged
:
1
b
}
}
}
请注意我用字体大小表示出的主次关系。
我们新增的标签
tag:
,即“标签”,描述了本物品包含的标签。
物品包含了什么标签呢?
Charged
:
1
b
弩已上弦。
所以:
-
/summon item ~ ~ ~ {Item:{id:"minecraft:crossbow", Count:2b, tag: {Charged:1b}}}
复制代码
召唤一个掉落物,为两个上弦的弩。
这样,你懂了吗?
没懂?
我再把上面的东西换种写法:
•
物品(Item):
•
物品ID(id):弩(crossbow)
•
物品的数量(Count):2(2b)
•
物品的NBT(tag):
•
是否装填(Charged):是(1b)
这样,是不是有立体感了?
2.5 列表
举个例子。
如何召唤一个拥有特定旋转角度的僵尸?
比如说,面向北方,仰角为30度什么的。
当然当然,我们可以召唤后再用
/tp
指令来进行旋转。
但是这里说的是,直接召唤时就是这种角度?
旋转也是用NBT“Rotation”存储的,而且数据格式是float,也就是“小数”(因为朝向是有小数的)。
但是熟悉数学的读者可能知道,再3维空间中的旋转需要用两个数字表示——
说白了就是,我们需要两个数字来表示一个方向,一个是水平旋转(东南西北),一个是垂直旋转(上下)。
但是你可能发现了。旋转只有一个标签名(或键)“Rotation”,但是我们有两个数值要存储。
难道要用上面的“复合内的复合”那种形式存储吗?
不是。
我们要用的是,列表(List)。
与复合标签一样,列表也可以用简单的一句话概括:
使用中括号“
[]
”作为开头与结尾,中间输入多个相同类型的元素,用逗号“
,
”隔开。
让我们比较傻地用“小朋友与糖”的例子说明:
假设有5名小盆友,他们每人有5块糖~
那么,如何用NBT的"列表"来表示呢?
小盆友的糖:[5, 5, 5, 5, 5]
像这样。这表示了每个小盆友都有5颗糖~
那么:
小盆友的糖:[1, 2, 3, 4, 5]
这是什么意思呢?
这代表了,第一个小盆友有1颗糖,第二个小盆友有2颗糖………以此类推。
我们把它放进NBT复合标签的大括号里,这事就成了:
{
小盆友的糖
:[
1
,
2
,
3
,
4
,
5
]
}
列表就这么简单。
那么,下面是旋转的NBT:
{
Rotation
:
[<水平旋转>
f
, <垂直旋转>
f
]
}
注意在列表里也是要加上数据类型的字母后缀的。
很容易得到——
-
/summon zombie ~ ~ ~ {Rotation:[180.0f, 35.5f]}
复制代码
召唤一只面向北,仰角为35.5度的僵尸。
(关于旋转问题,为什么180.0是北,详见
系列第二帖
)
接下来,这里有个有趣的东西。
“列表”中添加的是“
相同类型
的元素。
实际上,我们可以,在里面添加任何类型的元素。
不只是float(小数)
比如,数个“Int”。(整数)
或者数个long。(长整型)
或者……
数个复合标签。
对。就像我们可以在复合标签内套复合标签一样,我们也可以在列表中套复合标签。
一样是用英文逗号“,”隔开:
{
example:[
{xxx:yyy, aaa:bbb},
{xxx:yyy, aaa:bbb}
,
{xxx:yyy, aaa:bbb}
,
{xxx:yyy, aaa:bbb}
]
}
注意我用字体大小表现出的主次关系。
那么,什么时候用这种形式呢?
当我们有多个并列的项目,而且每一个项目都有复合标签的需要的时候——
比如,附魔。
附魔是用NBT“
Enchantments
”存储的。该标签是一个列表:
{
Enchantments
:
[
{
id
:"
sharpness
",
lvl
:
67
},{
id
:"
knockback
",
lvl
:
4
}]
}
在这个名为“
Enchantments
”的列表中包含了两个复合标签。每一个代表一个附魔。
在每一个复合标签中,“
id
”是附魔ID:
"
sharpness
"是锋利,而
"
knockback
"是“击退”。
"
lvl
"则是等级(Int整形)。
所以,上面的标签实际上描述了2个附魔——
锋利67与击退IV。
所以——
-
/give @a diamond_sword{Enchantments:[{id:"sharpness", lvl: 67},{id:"knockback", lvl: 4}]}
复制代码
给予所有玩家一把锋利Lv.67,击退IV的钻石剑(diamond_sword)。
我们只是添加了两个附魔,当然还可以再列表中加入更多。但这里就不举例了。
这样,你理解了么?
当然上面的例子不是最难的。
我们只是在一个列表里套了两个复合标签。
但实际上,我们还可以在列表中的复合标签中套另一个列表,里面再套复合标签……
咳咳,不折磨你们了。
2.6 数列
有一个有趣的事情,就是如果列表内装的数据类型是"Int(整数)","Byte(贼小的数(字节型))",或"Long(贼大的数(长整型))",我们的列表会有变化。
因为某种代码上的,跟我们无关的原因,这时我们需要在最前面加上一个东西:
example: [
B;
1
b
, 2
b
, 4
b
, 6
b
, 3
b
, 14
b
, 3
b
, 4
b
, 5
b
, 7
b
]
B;
代表了整个列表都是Byte。
……但是里面的东西你一样要加上“b”用来代表byte的数据类型。
同样,我们还可以有“
I;
”或“
L;
”,分别为“Int(整数)”与“Long(长整型)”。
注意这些前缀是大写的,别弄错了。
这些东西被称作
“数列”(Array)
。
而根据其列的数值的不同(最前面的字母不同),分为
I;
整型数列(int Array)
,
B;
字节型数列(byte Array)
,与
L;
长整型数列(long Array)
。
这玩意是比较少见的,一般最常用的就是UUID吧。
2.7 查看
教程的最后,让我们解决最根本的问题——
我该如何知道一个NBT叫啥名,该填什么值,又是什么数据类型?
简单简单。遇事不决看Wiki。
图中所示的是僵尸页面的“实体数据”段落。
在这里,你可以看到所有的标签,以及他们的类型
不就是Byte? 我们再上面的列表内也有相似的图标。
但是要补充几个:
意味着这是一个复合标签。
为列表。
是Byte数列。
是Int数列。
是Long数列。
就这些。
只要你看着上面的列表,你就能很容易给出我们在最开始举的那个僵尸的例子。
不过嘛,本教程刚刚才只是讲述了NBT最基础的格式——
接下来,我会举上几个例子来帮助你详细理解~
3. 示例
在开始看实例之前,我需要让你认清楚一件事——
你比不上命令生成器。
你可能知道,有一些可以用来生成NBT命令的网站,非常好用(虽然经常更新不及时。)
我想告诉你,对于复杂一些的NBT,人肯定是没有机器快的。使用这些生成器可以让你在很短的时间里,用可视化的方法快速生成指令。
所以,你看过本帖对NBT有了解之后,请记住善用这些生成器吧。
但是你要懂其中的原理——
不然对于生成器不全的,更新不及时的,或者出BUG的地方,你就无能为力了。
3.1 装有32个面包
16颗石头
的箱子
现在,我们要放置1只箱子,其中第3格放着32个面包,第5格放着16颗石头。
我们在
系列教程第十帖
讲过了用于放置方块的命令/setblock与方块状态(没看过的请回去看)。
当时我们说到,对于方块实体,方块状态无法涵盖所有内容。
那么,我们首先要找到箱子的NBT。像我们之前说的,这些玩意总是记录在Wiki上(只要没有忘了更新):
你看到的是箱子的NBT结构。你可以发现除了我们要的“
Items:当前容器内物品的列表。
”外,还有诸如“上锁”之类的神奇玩意。
但我们今天不管这些。我们只要在第3格放着32个面包,第5格放着16颗石头就好。
看向上图,我们可以发现用于存储箱子物品的NBT“
Item
”是一个由
数个复合标签
组成的列表,每一个复合标签代表了一个栏位的物品。
而包含在每个复合标签中的,用于存储物品信息的“
物品共通标签
”实际上我们已经讲过了,就是上面的“
两个弩
”的掉落物的那个,是一样的。
我们可以看到字段“
id
”存储了该物品是什么,标签“
Count
”存储了物品数量,而标签“Slot”则是该物品放在箱子的哪一格。
这里注意
Slot:1b
并不是箱子的第一格,而是第二格,因为栏位是
从0开始算
的。
所以依此类推,第3格是
Slot:2b
,第五格是
Slot:4b
。
所以:
{
Items
:
[
{
Slot
:
2
b
,
id
:
"
bread
"
,
Count
:
32
b
}
,
{
Slot
:
4
b
,
id
:
"
stone
"
,
Count
:
16
b
}
}
这就是我们要的NBT。
上面“
bread
”是面包,而“
stone
”是石头。
我们可以直观地看到,每一个物品都是一个复合标签,而它们都被装在一个列表里。
指令/setblock的NBT和/give一样,紧贴在物品的后面,而且你大概知道“chest”是箱子,那么:
-
/setblock ~ ~ ~ chest{Items:[{Slot:2b, id:"bread", Count:32b}, {Slot:4b, id:"stone", Count:16b}]}
复制代码
这就是我们要的指令。
3.2 超级无敌宇宙最强太古霸王剑
一个提示:3.2的内容在1.12.2及以下有一定差异
咳咳,第二个实例是,给予自己一把攻击力+64的钻石剑,其自定义名称为“超级无敌宇宙最强太古霸王剑”。
我们该寻找可以达成这一目标的标签名是什么呢?
在Wiki页面“
Player.dat格式
”与“
教程/NBT与命令标签
”中,给出了我们要更改的两项:
即自定义名称(来给剑加上中二的名字),与属性修饰器(给与剑“攻击+64”的属性)。
首先,让我们看向自定义名称:
我给的图片是镜像Wiki的,其数据类型的图标与刚才说的有区别,但没关系,它们大体相似。
我们可以看见“控制物品的自定义显示信息”的是一个复合标签,其中可以拥有3个子标签:
字符串
“
Name
”,用于控制
物品的名字
;
字符串
“
Lore
”,用于控制物品的描述;与最后的
整型Int
"
color
",这个是控制皮革铠甲的颜色的。在本例中用不上。
这个文字要求是
JSON文本
,由于我们还没说到JSON,所以这里我们就用它的无特殊格式简写,及用双引号引起来。
注意NBT本身还有一个用来表示这是个字符串的单引号,所以结果是我们要把文字用“'"”,即外侧单引号加上内侧的双引号。
我们不计划在本物品中添加自定义描述,所以"
Lore
"我们不填。
我们想要的自定义名称是“超级无敌宇宙最强太古霸王剑”——
所以,如果你看懂了本教程,应该能明白——
diamond_sword
{
display
:
{
Name
:'
"
超级无敌宇宙最强太古霸王剑
"'
}
}
描述了一个名称为“超级无敌宇宙最强太古霸王剑”的钻石剑(diamond_sword)。其中,由于自定义名称是字符串,所以我们用了引号""来引起来。
Name
是“
自定义名称
”的标签名,而它又是
display
的子标签。
这个解决了,看下一个,更改属性:
这里我们要用的是
“属性修饰器”(AttributeModifiers)
来更改属性。
这里要注意的是,属性修饰器这个东西不止你在用,游戏也在用。
比如,当你疾跑时获得的速度加成实际上就是属性修饰器的一种。
我们可以把上面的这个内容整理一下:
-
AttributeModifiers
:
(列表)
包含了物品的修饰属性。它将修改佩戴者或持有者的属性。
-
一个修饰器
-
AttributeName
:
(字符串)
要
修饰的属性。
-
Name
:
(字符串)
修饰符的名称。
-
Slot
:
(字符串)
指定修饰符产生效果的槽位。值只能为"
mainhand
(主手)"、"
offhand
(副手)"、"
feet
(装备栏:靴子)"、"
legs
(装备栏:护腿)"、"
chest
(装备栏:胸甲)"或"
head
(装备栏:偷窥)"。若不指定将会全装备栏通用。
-
Operation
:
(Int)
详见
属性修饰符
。0是“增加”,1是“乘以”,2是“最终倍乘”
-
Amount
:
(Double)
这个修饰符的数额。
-
UUID
:
(Int Array)
属性的
UUID
,以4个32位整数的形式存储。
上表改编自MC中文Wiki。
看一下上表,我们可以得到:
AttributeModifiers
就是我们属性修饰符的根标签,它是一个列表,可以包含数个属性修饰符,而每一个修饰符有以下标签:
这里我们要加的是攻击力“
generic.attack_damage
”。
Name
是该属性修饰器的名字。这个你不用填,我们以后会说;
Slot
是“该属性在哪里发挥作用。”因为这是个武器,所以肯定是主手“
mainhand
”发挥作用。
Operation
是运算,就是你要干什么。根据上表,我们是“增加”,所以填“
0
”。
Amount
就是修饰器要更改的值:我们要加“
64
”。
最后
UUID
,是这个属性修饰器的
唯一身份识别码(Universal User Identity)
我们有说过属性修饰符不只是你打的指令在用,游戏也在用。这个UUID就是游戏用来区分不同的属性修饰符的。
UUID是一个由4个数字——整型(int)组成的数列。
不过这里我们不多说。在这里你只要随便填上4个数就好:比如:
[I; 1,2,3,4]
这个数基本上可以乱编,因为你相当于在创建一个自定义的属性修饰符。你只要注意不用重复的就好了。
但是呢,有一些特定的UUID你是不能用的,因为它们被游戏占用了。它们被列在了Wiki
这里
。(不用担心,随便填撞见它们的概率微乎其微……)
综合以上标签,我们可以得到:
{
AttributeModifiers
:
[
{
AttributeName
:
"
generic.attack_damage
"
,
Slot
:
"
mainhand
"
,
Amount
:
64
d
,
Operation
:
0
,
UUID
:
[
I;
1
,
2
,
3
,
4
]
}
]
}
这描述了一个可以在玩家将其放置于主手时增加64点攻击力的属性修饰器。
好,就这样,把上面的两个标签合起来:
{
AttributeModifiers
:
[
{
AttributeName
:
"
generic.attack_damage
"
,
Slot
:
"
mainhand
"
,
Amount
:
64
d
,
Operation
:
0
,
UUID
:
[
I;
1
,
2
,
3
,4]
}
,
display
:
{
Name
:'
"
超级无敌宇宙最强太古霸王剑
"'
}
]
}
加入指令头,目标等,我们可以得到最终的命令:
-
/give dahesor minecraft:diamond_sword{display:{Name:'"超级无敌宇宙最强太古霸王剑"'},AttributeModifiers:[{AttributeName:"generic.attack_damage",Slot:"mainhand",Amount:64d,Operation:0,UUID:[I;1,2,3,4]}]} 1
复制代码
附录,注释,与外部链接
附录1 附魔ID
以下表格引自我的世界中文Wiki。
注意,下列表格可能由于更新不及时而导致过时,请以
Wiki本页面
为准。
最后同步时间:Java 1.16.5 (2021年4月)
点击一个魔咒可以查看其在Wiki的说明。
|
注释
【1】本帖内不区分执行者与执行地
【2】本标签一旦大于0,溺尸的转变就是不可逆的,离开水也没用。
【3】理论上,在一些情况中,即使你不在数字后添加用于确定数据类型的字母,系统也会自动补全。有些标签甚至接受不同类型的输入。
但是!
不填字母容易出BUG。所以,请总是填入字母。
所以就这样吧。
最后,各位,给点人气(盛满怨念)
以上。
Java 1.16.5/b 感谢的提醒,修改了一处错误
Java 1.16.5/d 更改了关于JSON的错误。
Java 1.16.5/e 添加了新示例。
Java 1.16.5/f 感谢@SPGoding 的纠错,改正了关于数列的很严重的错误。
Java 1.17/a 1.17版本升级完成