MC命令教程“真”从零开始(十一)NBT通俗演义(雾)
Dahesor
2024年06月27日 08:13
收录于文集
共16篇

这是原载于MCbbs论坛上的命令教程。随着论坛的消失,这系列教程经修缮(可能需要几天时间)后被迁移至此。

本文某种程度上是一个白话版的Wiki

作者不保证对本教程的维护和更新(全随兴致),请您自行判断内容是否过时。不过,若有更多问题欢迎在评论区提出

教程声明:

  1. 系列教程默认读者是按照顺序阅读的

  2. 教程默认读者有对Minecraft的基础了解

  3. 教程主要注重基础原理,实践内容极少

  4. 本篇教程适用于Java 1.20.4,但大部分内容也适用于1.13+,以及1.20.5+


前言

  • 浮空文字。

  • 自定义怪物。

  • 自定义的钻石剑。

  • 自定义刷怪笼。

  • 可以让玩家穿过的“幽灵方块”。

  • 自定义村民。

  • 自定义装备属性。

——打开任何一个我的世界的交流社区,你见到的最多的问题无外乎以上几个。(现在好点了,以前这是到处都是。) 所有这些东西,使用我们今天的教程——你都可以明白如何制作。 我们使用的技术,叫做“NBT” 我在这里保证只要你能从头到尾读完就一定能明白NBT怎么用。 不是什么特别难的东西。 但先不要着急。在说明NBT之前,我们得先了解,这玩意可以用在什么上面。 所以,在开始前,请容我花费较少的篇幅介绍一条指令—— 1. 召唤命令/summon 你知道么,我是很少用刷怪蛋的,因为我早已熟悉使用/summon指令来生成实体​ 本指令可以在确定的位置,生成一个实体,并可指定NBT标签: 格式:

代码块
CSS
自动换行
复制代码
/summon <实体ID> [<x> <y> <z>]
复制成功

(指令/summon格式,简化版) 如果你有好好看系列之前的内容,本指令的含义不言而喻: 只要将"<实体ID>"替换为一个实体,即可在后方的坐标

代码块
CSS
自动换行
复制代码
/summon blaze 10 5 -412
复制成功

在坐标 (10, 5, -412) 处召唤一只烈焰人 当然,我告诉你烈焰人的ID是blaze,那么如何找到你想要的ID呢? 比如,僵尸的?这当然要查wiki了 点此链接前往Wiki这里来查询所有实体的ID:​ (实际上调试页面也会表明当前看向的实体的ID) 我非常好心地把这些ID列了出来: 看看这些实体吧……你知道画和物品展示框实际上是实体而非方块吗?

图片截取于Minecraft Wiki (2024年6月/1.21),更新不及时请以Wiki为准

你可能注意到了位置是个可选的元素。当你不指定坐标时,这个实体会被召唤在指令的执行地:

代码块
CSS
自动换行
复制代码
/summon tnt
复制成功

在执行位置召唤一个已激活的TNT (你得小心了) 同理,“zombie”是僵尸:

代码块
CSS
自动换行
复制代码
/summon zombie
复制成功

在执行者的位置召唤一个僵尸。【注1】 现在,就进入正题了—— 你所召唤的僵尸,只是一个普普通通的僵尸,受重力影响,也没有穿着很酷的装备—— 因为你没有指定其NBT。

当不指定一项NBT时,游戏将将其设定为它的默认值。

下面,就让我们来探讨一下,如何加入NBT。

2. NBT标签与键值对

NBT(二进制命名标签,Named Binary Tags)是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(字符串化的二进制命名标签,Stringified NBT

本格式与上面的“.nbt”表达的是一种东西,只是用的是不同的两种语言罢了——一种给人读,一种给机器读。

——但是你也不用担心什么时候是不是该叫“SNBT”,什么时候又该叫“NBT”——

在命令相关的语境中,“NBT”总是在指其SNBT的格式。

那么,回到正题上来。

NBT长啥样呢?它规定了什么?又如何加入到命令中呢?

我在本系列教程的第1篇提到过一个有关奶的例子。

这个例子说的是,如何精确地描述一杯奶。

我们在这里,可以使用像下方所示的一样,用多个项目与值来描述这杯奶:

  • 奶的种类:牛奶

  • 奶的量:400ml

  • 装奶的容器是否密封:

  • 奶的产源:卖奶的老奶奶

  • 离过期还有:3天

这样一来,你应该对我桌子上放的这杯奶有点了解了。 NBT差不多就是一样的东西: 让我们简单地描述某一只僵尸:

  • 当前生命值16.8滴血#

  • 是不是小僵尸

  • 是否可以破坏门

  • 剩余着火的时长5秒(100游戏刻)

  • 是否无敌

  • 转变成溺尸之前的剩余时间*:10秒(200游戏刻)

  • 头部的装备钻石头盔

  • 腿部的装备:……

  • 面向的方向:……

注释:

#虽然不会显示,但是生命值是有小数的。

*当僵尸在水中时,会在一定时间后转变为溺尸。

由于可以说的东西太多,我只列了一些。但各位可以从上面的描述中,想象出一只确定的僵尸了吧。 NBT说的大概就是这样的东西。 上面的每一个描述,都被称为“键值对(Key-Value Pair) 这是个挺起来好像很强的名字,但实际上说的只是—— 冒号前面的“描述”被称为   ,也叫“标签名”——通俗地说就是“描述了本项目在讲什么。

冒号后面的东西就是         “——通俗地说就是“对于这个描述(或者说键标签名),我们要它是多少”

因为 “键”与 “值”总是成对出现,所以我们叫每一对为键值对

聪明的你已经发现,这和上一篇/setblock讲的方块状态是差不多的东西。没错,两者都是键值对,没什么本质上的差别。

接下来的事情非常简单,我们只需要把上面的描述换成NBT所接受的特定的英文的就好了。这和上一篇的方块状态名差不多,比如facing=east中的facing。

方便起见,让我们把上面的列出的9个键值对简化为以下4个:

  • Health: 16.8                                  (当前生命值16.8滴血)

  • IsBaby: 1                                       (是小僵尸)

  • Fire: 100                                       (剩余着火事件(刻):100游戏刻)

  • DrownedConversionTime: 200       (转变成溺尸之前的剩余时间为200游戏刻)

——以上面的“转变成溺尸之前的剩余时间”为例——

代码块
CSS
自动换行
复制代码
DrownedConversionTime:200
复制成功

恭喜你,得到了第一个NBT标签

该如何把NBT加入/summon命令中呢?看格式:

代码块
CSS
自动换行
复制代码
/summon <实体ID> [<x> <y> <z>] [<NBT>]
复制成功

只要简单地把NBT放到最后就好了

但是等等,想要让标签生效,你需要把它们装进花括号里。这和上一篇的方块状态一样,只不过是花括号而不是方括号:

代码块
CSS
自动换行
复制代码
/summon zombie ~ ~ ~ {DrownedConversionTime:200}
复制成功

在执行位置生成一只僵尸,在10秒(200刻)后会变成溺尸

僵尸,或者说大部分实体还可以有一个叫做TicksFrozen的标签,代表在接下来几游戏刻内该实体会处于冻结状态(就是在细雪里带了太久会掉血):

举一反三:

代码块
CSS
自动换行
复制代码
/summon zombie ~ ~ ~ {TicksFrozen:400}
复制成功

在执行位置生成一只僵尸,在接下来400刻或者20秒内持续冻伤

和上一篇的方块状态一样,指定多个标签时,需要用逗号隔开:

代码块
CSS
自动换行
复制代码
/summon zombie ~ ~ ~ {TicksFrozen: 100, DrownedConversionTime: 200}
复制成功

在执行位置生成一只僵尸,在接下来100刻或者5秒内持续冻伤,且200刻(10秒)后变成溺尸

注意NBT是无视键名和值以外的空格的,也就是说一下两条NBT是等价的:

  • {one:100,two:200}

  • {one:    100,   two  :    200}

所以为了方便阅读,可以在冒号以及逗号后面适当加入空格

此外,NBT所有的键名区分大小写,而且冒号和逗号使用的是英文的半角符号 ":" 和 "," 不是中文输入法输入的比较宽的标点,一定注意。

还有,这种花括弧包围的NBT的顺序是完全无所谓的,哪一个放前面都行。

那么,举一反三,看一下剩下的3个吧。

生命值标签叫做"Health",上面说是16.8滴血。

燃烧时间,"Fire"是100游戏刻(20秒)。

至于最后的是否是小僵尸"IsBaby",对于这种“要么是要么不是”的真或假二选一问题,NBT用0代表否,1代表是

所以,是否是小僵尸 "IsBaby"为1(是小僵尸)。

所以说,我们很轻松地就得到了以下三项:

  • Health:16.8

  • Fire:100

  • IsBaby:1

所以上面的三对标签分别描述了“生命值为16.8,剩余燃烧时间100刻”,与“是小僵尸”喽?

不不不,不是。我们不这样写。

我们要加上一些东西:

  • Health:16.8f

  • Fire:100s

  • IsBaby:1b

后面加上的三个字母“f”,“b”,“s”是什什么意思?

为什么要在特定的标签后添加这3个字母?为什么前面的“DrownedConversionTime:200”后面就什么都不用添加?【3】

这些字母是“数据类型(Data Type)”。

3. 数据类型

什么是加在数据后面的字母?

这种加在数字后面的字母你应该不陌生。

比如——

400L

——400升

我的意思是,那些数字后的字母,有点像是这个数字的“单位”。

当然,这完全不是一种东西。

它们是“数据类型”。

什么是数据类型?

容我这么解释:

电脑存储数据是需要空间的,而不同的数据需要的“空间”的大小类型有所不同。

比如说,“是否是小僵尸”这个标签只有两种可能——要么是要么不是。我们只需要一个很小的空间就能把它存进去——因为只有两种可能,0或1。

但,像是“着火时间”这个标签所需要的电脑空间就要大得多,因为我们要存储可能的大量不同的时间值,而不仅仅是“0或1”。

但由于“着火时间”这个标签的单位是游戏的最小时间单位“游戏刻”,所以,是不可能出现小数的。

这一点就和“生命值”不同。虽然我们无法在血条上看到小数的生命值,但是只要你对游戏有一定的理解就知道生命值是有小数的。

这里就又是一个不同之处。“着火时间”是不会有小数的,我们为它分配的存储空间也不需要担心小数的问题,可以节省很多空间。

但是“生命值”是有小数的,我们需要一个不同类型的空间来存储这个数值。

比如如果给“是否是小僵尸”这种只有两种可能的标签,只需要分配一个小空间就行了,不然浪费。而对于生命值这种需要存储小数的标签,就需要大一点空间,不然装不下

以上这多种不同的存储空间,就被称为“数据类型(Data Type)

上方在数字后面的字母,就代表了,该标签使用的是什么样的数据类型

我们共有7种不同的数据类型,用于存储不同种类,不同大小的数据,均列在下图:

NBT数据类型表

如果上图无法显示,请看Wiki页面。

先不要管复合类这些东西,只看独立类

生命值是"单精度浮点型",或者说“小数”,所以我们要在后面加上“f”:

(先不要管该如何知道一个标签的数据类型是什么的问题,这个我们后面说。)

剩下的3个标签同理:

Health:16.8f

Fire:100s            (Fire是短整型,加后缀s)

IsBaby:1b            (IsBaby是字节型,加后缀b。

DrownedConversionTime:200       (这个是整型,所以不用加后缀,TicksFrozen同理)

上面的4对标签正确地描述了一只僵尸的生命值,燃烧时间,小僵尸,以及转化溺尸的时间。

数据类型的后缀是不需要区分大小写的。IsBaby:1b 与 IsBaby:1B 都可以。

接下来,我们要做的就是把它们安进命令里就好了:

代码块
CSS
自动换行
复制代码
/summon zombie ~ ~ ~ {Health: 16.8f, Fire: 100s, IsBaby: 1b, DrownedConversionTime: 200}
复制成功

在原地召唤一只生命值16.8,会继续着5秒的火,并会在10秒后变成溺尸的小僵尸

就这样。

同样,我们可以生成一只无敌而且有手臂的盔甲架。

ShowArms”是是否拥有可以持有东西的手臂,是字节型“Byte”。

Invulnerable”是是否无敌。同样是字节型“Byte”。

所以,根据上面的例子举一反三——

代码块
CSS
自动换行
复制代码
/summon armor_stand ~ ~ ~ {Invulnerable:1b,ShowArms:1b}
复制成功

生成一个无敌且有手的盔甲架。

你可能知道有一条命令叫做“/give”,其作用是给予玩家物品:

代码块
HTML
自动换行
复制代码
/give <目标> <物品ID> [<数量>]
复制成功

非常简单,“<目标>”应是一位在线玩家的ID或选择器。物品ID与在系列第10篇​讲过的方块ID差不多,只要按下F3+H打开更详细的提示即可查看。 数量为该物品的数量,不输入默认为1个: 例:

代码块
CSS
自动换行
复制代码
/give @a stone 10
复制成功

给与所有玩家10颗石头

代码块
CSS
自动换行
复制代码
/give Dahesor diamond
复制成功

给予 Dahesor 1颗钻石 很好理解,不多说。 本指令也是可以添加NBT的。 但是你可能会发现,命令格式中并没有NBT的位置。 在本命令中,NBT应被添加到物品ID的后面,没有空格,因为这相当于是一个元素:

代码块
CSS
自动换行
复制代码
/give 目标 物品ID{NBT} 数量
复制成功

注意:以下物品相关内容仅适用于1.20.4及以下。1.20.5以后转为使用组件,下面关于物品NBT的内容已不再适用于最新版。关于组件请查询 网页链接​

比如,弩有一个标签Byte类型的标签“Charged”用于表示弩有没有上弦。 这个只有两种可能性。其中1为“真”,0为“假”的,我们能得到:

代码块
CSS
自动换行
复制代码
/give @a Dahesor minecraft:crossbow{Charged:1b} 1
复制成功

给予所有玩家一个上弦的弩(虽然射不出来,因为没有指定装填了什么)

反过来写Charged:0b就是没有上弦,也就是默认状态可以不用填

好,你应该理解了。

实际上,/give的原理是在目标的精确位置生成一个只有目标才捡的起来的凋落物。

掉落物也是一种实体,其实体ID为“item”(就是物品的意思)。

所以,在知道/give的原理以后,我们也可以用/summon指令直接召唤这样一个凋落物实体。

这里我们就说一下如何得知一个实体可以有的NBT标签——其实你可能已经知道了,就是查Wiki。

在Wiki上查询“物品”关键词,并根据歧义提示找到页面物品(实体)

我们需要的是物品(实体)而不是物品栏里的物品

往下翻找到数据值的部分:

数据值

实体数据就是我们需要的内容了(详见Wiki: 物品(实体)#数据值​):

注意本图片的内容是1.20.4以下的数据

这里有item这个实体所有可以拥有的NBT标签。注意上面有一个“实体共通标签”,这里的标签是所有实体都拥有的(比如刚才用过的Fire)。此处先不展开,先看下面item实体独有的标签。

Wiki中所有的实体其页面最下方都会有上图所示的NBT树。读懂这种结构并不难。上面介绍数据类型的时候,就已经在列表最左侧给出了所有数据类型的图标。

通过阅读描述可知,上面的PickupDelay是“剩余的不能被捡起的时间”。类型是它左侧的图标,根据上面的表格,这个"S"的图标是短整型Short,需要加上s后缀。所以如果你需要生成一个10秒(200刻)内无法被捡起的物品,就是:

代码块
CSS
自动换行
复制代码
/summon item ~ ~ ~ {PickupDelay:200s}
复制成功

应该说清楚了吧?如果你想要定义其他的NBT也是同理。

不过如果你直接执行上面的命令,会显示“生成了新的空气”。这是因为你没有指定该凋落物到底是什么物品。

所有凋落物,无论弩还是苹果,都是用item一个实体。至于item究竟是什么凋落物保存在它的NBT中。这个NBT叫做Item。注意这个是键名,不是实体ID,字母I是大写的。如果你不写的话,就会默认为空气——啥都没有。

Item这个标签是什么类型呢?根据左侧的图标配合数据类型表我们得知,这是一个“复合标签”。这又是什么鬼?

4. 复合标签

什么是复合标签?我们已经探讨过的标签只有一个数值。但有的时候一个数值不足以描述我们需要的描述的内容。复合标签的“值”不是一个数字,而是很多其他的标签。这有点像是在套娃。

比如,我们需要使用NBT标签来定义一个学生,需要记录年龄和学号。

那么如果规定年龄写作Age,学号写作ID,那么套用刚才的逻辑:

代码块
CSS
自动换行
复制代码
{Age: 17, ID: 6182}
复制成功

就规定了一个年龄17岁,ID为6182的学生,同理还可以有:

代码块
CSS
自动换行
复制代码
{Age: 21, ID: 2164}
复制成功

年龄21,ID为2164

这就是复合标签了。

什么?这和刚才我们放在/summon后面的不是一个东西么?没错,那个也是复合标签。所谓的复合标签,其实就是这个使用{}花括弧包围的,内部有多个键值对的东西!

只不过,复合标签是可以套娃的。一个复合标签可以套进另一个复合标签里。比如我们规定学生的键名写作student,那么:

代码块
CSS
自动换行
复制代码
{student: {Age: 21, ID: 2164}}
复制成功

诶,这样就把一个复合标签套了进去。这里,student存储的不再是一个数值,而是一整个复合标签,里面包含了其他的标签。

比如我们要描述一个职位是班长的学生,假设我们把职位的键名写作position,那还可以有:

代码块
CSS
自动换行
复制代码
{student: {Age: 21, ID: 2164}, position: "班长"}
复制成功

上面的这条NBT描述了一个职位是班长的同学。哪位同学呢?年龄21学号2164的这位。

你或许可以类比为填表格:

像是在填表格

Age(年龄)和ID(学号)都有自己的值,不过它们都是student的一部分。

当一个键名下面有多个需要存储的属性时,就会使用复合标签。

不知道我解释清楚没有。

当然,可以套一层就可以套两层,也可以有:

代码块
CSS
自动换行
复制代码
{student: {Age: 21, ID: 2164, data: {height: 181.4f, weight: 65.6f }}, position: "班长"}
复制成功

这里我们在student里面加了一个data,又是一个复合标签。这样就套了3层。

回到生成物品实体的问题上来,我们再看一遍NBT树:

注意Item标签

那么该如何生成一个物品为弩的凋落物实体呢?这是由Item这个标签决定的。这是一个复合标签。NBT树在这里分叉,显示出其下有所附属的子标签。

Count是堆叠的物品数量,类型是Byte(字节型),要加后缀b。

id是物品或方块的ID。类型是字符串,需要用双引号包围。

tag则是物品拥有的NBT,也是一个复合标签。根据不用的物品下面可以有不同的内容,不过大部分物品都是不用写的。

这里先无视tag,我们该如何生成一个弩的凋落物?弩的id上面已经说过,是crossbow,因此:

代码块
CSS
自动换行
复制代码
/summon item ~ ~ ~ {Item: {id:"crossbow", Count: 1b}}
复制成功

同理,stone是石头:

代码块
CSS
自动换行
复制代码
/summon item ~ ~ ~ {Item: {id:"stone", Count: 43b}}
复制成功

生成掉落物实体,为43块石头

你也可以把其他NBT标签组合起来。比如apple是苹果:

代码块
CSS
自动换行
复制代码
/summon item ~ ~ ~ {Item: {id: "apple", Count: 5b}, PickupDelay:100s}
复制成功

生成5个苹果的物品实体,5秒(100刻)之后才可以捡起来

现在,该如何生成一个上弦的弩的物品实体呢?我们在上面的/give命令可以直接把{Charged:1b}放在id的后面。不过当时我们可以直接给物品赋予NBT。而这里{Charged:1b}是描述“弩”的,而不是描述物品实体的——没错,{Charged:1b}应该是tag的一部分:

代码块
CSS
自动换行
复制代码
/summon item ~ ~ ~ {Item: {id:"crossbow", Count:1b, tag: {Charged: 1b}}}
复制成功

这回生成的弩凋落物就是上弦的了。注意我们把{Charged:1b}套在了tag里。这个结构是这样的:

  • 物品信息:

    • ID:弩

    • 数量Count:1

    • 物品所带数据tag:

      • 上弦:是

这是一个层层嵌套的关系。

那么,该怎么查询每个物品都有什么NBT呢?当然还是查wiki啦!需要注意的是,本教程内用来举例的物品NBT已经被更新的物品组件替代了。各个物品页面也只会显示新版组件。不过你还是可以在这个叫做“物品格式​”的页面找到1.20.4以及以前的旧版的NBT:

翻到下面这一部分,就可以找到一样的NBT树:

弩的独有NBT

这里的NBT树的最上层是tag。这种最上层我们也叫根标签。下面标出了Charged可以表示弩是否装填。注意其左侧的数据类型图标是T/F。这是布尔值的意思,,就是要么真要么假的数值,只有两种可能性(系列第九篇曾经说过)。

NBT格式没有给布尔值额外分配一个数据类型。所有布尔值都是用字节型存储的。1b就是真,0b就是假。所以看到这种图标记住这一点就行了。

就这样。再比如你想用/summon生成一只有鞍的马(马的ID是horse),那么就去查找wiki的马这一页​,翻到下面实体数据:

马的实体数据

我们可以看到,SaddleItem这个标签决定了鞍物品。它是一个复合标签,内容和刚才用了几次的Item一样。

想要让马可以骑我们还需要它是被驯服的。所以Tame决定了是否被驯服我们就把它设为1b,也就是真。

鞍的ID是saddle,所以就有:

代码块
CSS
自动换行
复制代码
/summon horse ~ ~ ~ {SaddleItem: {id: "saddle", Count: 1b}, Tame: 1b}
复制成功

生成一只有鞍的,已经驯服的马。

额外需要说明的是,有些复合标签可以是空的,也就是说里面什么都没有,只有一对大括号:

代码块
CSS
自动换行
复制代码
{SaddleItem: {}, Tame: 1b}
复制成功

这里SaddleItem就是一个空的复合标签。

5. 列表与数列

如何召唤一个拥有特定旋转角度的僵尸? 比如说,面向北方,仰角为30度什么的。 当然当然,我们可以召唤后再用/tp指令来进行旋转。 但是这里说的是,直接召唤时就是这种角度?

通过查阅wiki上的实体共通标签​,我们发现所有实体有一个叫做Rotation的标签,决定了他当前的旋转角度:

实体共通标签。这些标签所有实体都有

不过,Rotation的数据类型,通过对照图标和上面的NBT数据类型表可以看出,它是一个列表。

列表是什么呢?

列表允许存储数个相同类型的值。

比如说你想要用NBT记录一周内每一天的平均温度,那你需要7个标签:

代码块
CSS
自动换行
复制代码
{temp:{day_1: 16, day_2: 15, day_3: 25, day_4: 17, day_5: 10, day_6: 8, day_7: 4}}
复制成功

列表允许你将这种有序的数据打包成一个标签,使用中括号[ ]包围,每项用逗号隔开:

代码块
CSS
自动换行
复制代码
temp: [16, 15, 25, 17, 10, 8, 4]
复制成功

其中,16就是temp这个列表的第一项,15是第二项,等等等等。只要是相同类型,你就可以放进列表里。

回到旋转上。熟悉数学的读者应该知道,3维空间中的旋转需要用两个数字表示——

说白了就是,我们需要两个数字来表示一个方向。一个是水平旋转(东南西北),一个是垂直旋转(上下)。

因此Rotation是一个拥有两个元素的列表。第一项是水平旋转,第二项是俯仰角。通过查阅上面的NBT树可以得知,元素类型为float单精度浮点数,需要加f作为后缀(因为旋转是可以有小数的)

所以我们可以得到:

代码块
CSS
自动换行
复制代码
/summon zombie ~ ~ ~ {Rotation: [180.0f, 35.5f]}
复制成功

召唤一只面向北,仰角为35.5度的僵尸

(关于旋转问题,为什么180.0是北,详见系列第二帖)

通过调整Rotation列表内的两个数值,你可以控制僵尸朝向任意方向生成。

诚然,确实可以使用复合标签来让Rotation拥有两个数值。然而复合标签内的每一个值都需要有自己的标签,所以会浪费一定的存储空间和可读性,对于拥有几百个元素的长列表更是如此。(想象一下从day_1写到day_999)

那么,你知道什么是列表了,什么是数列呢?

数列其实就是内容为Byte(字节型),Int(整型),或者Long(长整型)的列表。

因为某些你不需要了解的原因,NBT为这三种情况独立创建了一种数据类型,也就是字节型数列整型数列,和长整型数列

这三个类型要求你在数据之前加上前缀,大写的B或者I或者L,再加一个分号,比如:

代码块
CSS
自动换行
复制代码
{UUID: [I; 3619, 31323, -36162, -889253]}
复制成功

这个UUID就是一个整型数列。把I换成B:

代码块
CSS
自动换行
复制代码
{Byte_Array: [B; 1b, 2b, 4b, -8b, 100b]}
复制成功

就是一个字节型数列了。

数列的使用频率比较低,可以不用太过考虑。

一个需要指明的事实是,虽然字节型整型,和长整型有数列,但不代表它们不能成为列表。你照样可以写:

代码块
CSS
自动换行
复制代码
{List: [1, 2, 3, 4]}
{Bytes: [1b, 2b, 3b, 4b]}
{Longs: [1L, 2L, 3L, 4L]}
复制成功

用列表存储4个整型。这是没有问题的。只要列表中的每一项都是一个类型就可以。

你也可以有double列表,或者字符串列表:

代码块
CSS
自动换行
复制代码
{StringList: ["我把", "一个", "句子", "分成", "了", "很多", "个", "词汇", "!"]}
{DoubleList: [74.2836182d, 86.1927333d, 9.0d]}
复制成功

6. 套娃时间

这里有个有趣的东西。

“列表”中添加的是“相同类型的元素。

可没说“只能添加独立的元素”。

我们可以,使用列表存储任意类型的数个一样类型的值。

比如……

数个复合标签。

对。就像我们可以在复合标签内套复合标签一样,我们也可以在列表中套复合标签。

一样是用英文逗号“,”隔开:

代码块
CSS
自动换行
复制代码
{empty_objs:[{}, {}, {}, {}]}
复制成功

注意这里,empty_objs这个列表存储了4个空的复合标签。我们当然也可以给每个标签填上不同内容:

代码块
CSS
自动换行
复制代码
{example:[{one: 1, yes: 4d}, {}, {only: 1b}, {yes: "yyy", ki: 3.3f}]}
复制成功

这里第一个复合标签里有两个标签,第二个是空的,第三个有一个标签,第四个又有两个标签。

那么,什么时候用这种形式呢?

当我们有多个并列的项目,而且每一个项目都有复合标签的需要的时候——

比如,附魔。

通过查找wiki的通用物品标签​,你可以找到:

所有物品都可以使用这些NBT(1.20.4以前)

通过阅读这个NBT树可以得知,附魔是用Enchantments存储的。该标签是一个列表。

我需要再次提醒你,关于物品的NBT只适用与1.20.4以及之前,下面关于附魔的内容不适用于最新版的Minecraft。关于最新版请查阅物品堆叠组件​,这个后面有机会我们再讲。

好的,那么阅读上面的NBT树,可以发现Enchantments是一个列表,其每一项都是一个复合标签,描述了一个附魔。每个复合标签都包含两个信息,一个id是字符串,为某种附魔的ID,另一个lvl是短整型(加后缀s),是这个附魔的等级。

那么,如果我们想要一把锋利10,击退4的钻石剑,就需要在列表内加入两个元素,一条关于锋利,一条关于击退。

在Wiki上的Java版数据值​的这个页面可以找到所有的ID,包括附魔ID:

部分魔咒ID

我们可以看到锋利是sharpness,击退是knockback。因此就可以写出:

代码块
CSS
自动换行
复制代码
{Enchantments: [{id: "sharpness", lvl: 10},{id: "knockback", lvl: 4}]}
复制成功

这条NBT就规定了锋利10加上击退4。你当然可以加入任意多其他的附魔。只需要在列表内继续加入复合标签就行了,照葫芦画瓢。

钻石剑的id是diamond_sword,所以就可以使用/give命令给予自己一把锋利10加上击退4的钻石剑了:

代码块
CSS
自动换行
复制代码
/give @p diamond_sword{Enchantments:[{id: "sharpness", lvl: 10},{id: "knockback", lvl: 4}]}
复制成功

你也可以用/summon生成一个锋利10加上击退4的钻石剑掉落物,只需要多套几层娃:

代码块
CSS
自动换行
复制代码
/summon item ~3 ~ ~ {Item: {id: "diamond_sword",tag:{Enchantments: [{id: "sharpness", lvl: 10},{id: "knockback", lvl: 4}]},Count: 1b}}
复制成功

在x轴~3的位置生成锋利10加上击退4的钻石剑物品

我们上面讲的是列表内套复合标签。但是也完全可以列表内套其他列表,套数列,或者套列表里面套复合标签再套列表……结构可以变得十分复杂。

不过,游戏使用的大部分NBT都无外乎是几个列表套着复合标签什么的

这样,我讲清楚了么?

练习题

1. 生成一只不受重力影响的僵尸

步骤提示:

  1. 先试着生成一只普通的僵尸,用/summon

  2. 找一下哪条NBT控制了实体是否受到重力影响?(提示:在实体共通标签里)

  3. 试着生成一只不受重力影响的僵尸,打它一拳确认你成功了。

2. 用/summon生成一只骷髅。让它的主手拿着锋利3,耐久1的钻石剑,副手拿着3块石头。你需要使用1.20.4或以下的版本来做这件事情(不要太低!)

这次先不用你自己查wiki,我给你补一个NBT树的图片。这是骷髅的NBT中你需要用到的部分:

AI生物共通标签

你所需要的所有信息都在上面的图片以及本篇提及过的其他图片和文字中了。

步骤提示:

  1. 先试着生成一只普通的骷髅。骷髅的ID是什么?怎么用/summon(不带NBT)?

  2. 阅读骷髅的NBT树。哪一项控制了它的双手拿什么物品?

  3. 什么是物品共通标签?这个好像之前出现过很多次。

  4. 尝试生成一只骷髅,拿着普通的钻石剑和3块石头。

  5. 该怎么让钻石剑有锋利3,耐久1?你需要翻找附魔ID,还需要控制数值。

  6. 现在尝试把附魔内容添加给钻石剑。你或许可以用/give命令测试。

  7. 尝试生成一只骷髅。让它的主手拿着锋利3,耐久1的钻石剑,副手拿着3块石头。

附加题

使得上面生成的骷髅被杀死时,百分百掉落其手持物品。杀掉它看看有没有成功。

3. 使用/summon生成一只被剪过毛的小羊。这里需要你去查阅wiki来获得信息。没错,使用NBT是可以做到的。

提示:你需要羊的NBT。在哪里能找到呢?

上面的问题,如果你不确定自己有没有成功,或者需要帮助,可以在评论区留言。

在结束这一篇之前,我会举上两个特别的例子。这里有些新内容

7. 实例

7.1 装有32个面包16颗石头的箱子

现在,我们要放置1只箱子,其中第3格放着32个面包,第5格放着16颗石头。

我们在系列教程第十帖讲过了用于放置方块的命令/setblock与方块状态(没看过的请回去看)。

当时我们说到,对于方块实体,方块状态无法涵盖所有内容。

那么,我们首先要找到箱子的NBT。像我们之前说的,这些玩意总是记录在Wiki上(只要没有忘了更新):

不是所有的方块都有NBT

你看到的是箱子的NBT结构。你可以发现除了我们要的“ Items:当前容器内物品的列表。”外,还有诸如“上锁”之类的神奇玩意。

但我们今天不管这些。我们只要在第3格放着32个面包,第5格放着16颗石头就好。

看向上图,我们可以发现用于存储箱子物品的NBT“Item”是一个由数个复合标签组成的列表,每一个复合标签代表了一个栏位的物品。

而包含在每个复合标签中的,用于存储物品信息的“物品共通标签”实际上我们已经讲过了,就是上面的“”的掉落物的那个,是一样的:

物品共通标签

我们可以看到字段“id”存储了该物品是什么,标签“Count”存储了物品数量,而标签“Slot”则是该物品放在箱子的哪一格。

这里注意Slot:1b并不是箱子的第一格,而是第二格,因为栏位是从0开始算的。Slot: 0b才是第一格。

所以依此类推,第3格是Slot:2b,第五格是Slot:4b

所以:

代码块
CSS
自动换行
复制代码
{Items:[{Slot:2b, id:"bread", Count:32b}, {Slot:4b, id:"stone", Count:16b}}
复制成功

这就是我们要的NBT。

上面“bread”是面包,而“stone”是石头。

我们可以直观地看到,每一个物品都是一个复合标签,而它们都被装在一个列表里。

指令/setblock的NBT和/give一样,紧贴在物品的后面,而且你大概知道“chest”是箱子,那么:

代码块
CSS
自动换行
复制代码
/setblock ~ ~ ~ chest{Items:[{Slot:2b, id:"bread", Count:32b}, {Slot:4b, id:"stone", Count:16b}]}
复制成功

这就是我们要的指令。

本命令在1.20.5以及之后失效。

7.2 超级无敌宇宙最强太古霸王剑 本内容在1.20.5以及之后失效。 咳咳,第二个实例是,给予自己一把攻击力+64的钻石剑,其自定义名称为“超级无敌宇宙最强太古霸王剑”。 我们该寻找可以达成这一目标的标签名是什么呢? 在上面物品格式里。我再放一遍图片:

物品通用NBT数据

即自定义名称(来给剑加上中二的名字),与属性修饰器(给与剑“攻击+64”的属性)。 首先,让我们看向自定义名称:

我们可以看见“控制物品的自定义显示信息”的是一个复合标签,其中可以拥有3个子标签:

字符串“Name”,用于控制物品的名字;字符串“Lore”,用于控制物品的描述;与最后的整型Int "color",这个是控制皮革铠甲的颜色的。在本例中用不上。

这个文字要求是JSON文本,由于我们还没说到JSON,所以这里我们就用它的无特殊格式简写,及用双引号引起来。

注意NBT本身还有一个用来表示这是个字符串的单引号,所以结果是我们要把文字用“'"”,即外侧单引号加上内侧的双引号:

代码块
CSS
自动换行
复制代码
'"应该用单引号+双引号括起来"'
复制成功

我们不计划在本物品中添加自定义描述,所以"Lore"我们不填。

我们想要的自定义名称是“超级无敌宇宙最强太古霸王剑”——

所以,如果你看懂了本教程,应该能明白——

代码块
CSS
自动换行
复制代码
diamond_sword{display:{Name:'"超级无敌宇宙最强太古霸王剑"'}}
复制成功

描述了一个名称为“超级无敌宇宙最强太古霸王剑”的钻石剑(diamond_sword)。

这个解决了,看下一个,更改属性:

这里我们要用的是“属性修饰器”(AttributeModifiers)来更改属性。

关于属性修饰器我们会在后续的内容中详细提到。所以这个实例没看懂也没关系。

这里要注意的是,属性修饰器这个东西不止你在用,游戏也在用。

比如,当你疾跑时获得的速度加成实际上就是属性修饰器的一种。

我们可以把上面的内容整理一下:

我们可以得到:

AttributeModifiers 就是我的们属性修饰符的根标签,它是一个列表,可以包含数个属性修饰符,而每一个修饰符有以下标签:

AttributeName是要修饰的属性,你可以在Wiki这里查找。

这里我们要加的是攻击力“generic.attack_damage”。

Name是该属性修饰器的名字。这个你不用管,我们以后会说;

Slot是“该属性在哪里发挥作用。”因为这是个武器,所以肯定是主手“mainhand”发挥作用。

Operation是运算,就是你要干什么。根据上表,我们是“增加”,所以填“0”。

Amount就是修饰器要更改的值:我们要加“64”。

最后UUID,是这个属性修饰器的唯一身份识别码(Universal User Identity)

我们有说过属性修饰符不只是你打的指令在用,游戏也在用。这个UUID就是游戏用来区分不同的属性修饰符的。

UUID是一个由4个数字——整型(int)组成的数列。

如果你感兴趣的话可以在Wiki这里​找到关于UUID更多信息。

不过这里我们不多说。在这里你只要随便填上4个数就好:比如:

[I; 1,2,3,4]

这个数基本上可以乱编,因为你相当于在创建一个自定义的属性修饰符。你只要注意不用重复的就好了。

但是呢,有一些特定的UUID你是不能用的,因为它们被游戏占用了。它们被列在了Wiki这里​。(不用担心,随便填撞见它们的概率微乎其微……)

综合以上标签,我们可以得到:

代码块
CSS
自动换行
复制代码
{AttributeModifiers:[{AttributeName:"generic.attack_damage",Slot:"mainhand",Amount:64d,Operation:0,UUID:[I;1,2,3,4]}]}
复制成功

这描述了一个可以在玩家将其放置于主手时增加64点攻击力的属性修饰器。

好,就这样,把上面的两个标签合起来:

代码块
CSS
自动换行
复制代码
/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
复制成功

这就是最终命令

结束。

以上就是本篇的全部内容了。若有建议或问题请务必提出。