Minecraft(我的世界)中文论坛

 找回密码
 注册(register)

!header_login!

只需一步,立刻登录

查看: 2741|回复: 34

[教程] 【命令】命令教程“真”从零开始 ( X ) 我就不信不能用大白话讲清楚NBT [1.17]

[复制链接]
Dahesor 当前离线
积分
2079
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-2-5
查看详细资料
发表于 2021-4-24 15:50:17 | 显示全部楼层 |阅读模式

您尚未登录,立即登录享受更好的浏览体验!

您需要 登录 才可以下载或查看,没有帐号?注册(register)

x
本帖最后由 Dahesor 于 2021-9-8 03:37 编辑

[命令] 命令教程“真”从零开始 ( X ) 我就不信不能用大白话讲清楚NBT


声明:
1. 本系列教程默认读者拥有关于Minecraft游戏本身的基础了解。
2. 本系列全部教程绝对适用于当前Java最新版(1.17)。
3. 本系列教程致力于基础原理而非使用方法,因为某些原因,这是本声明里最重要的一条。


你可能发现,本帖上方的声明少了一条。
对,就是那条”本教程默认读者读过之前的内容“的那一条。
本帖是特例。下一帖那个声明还会回来的。
为什么没了呢?
因为NBT是一个很多人在问的问题,而我想让这一帖再普及一些——
所以,在本帖中,我会尽可能地减少看懂它所需要的知识背景——
让我们开始吧。




前言
  • 浮空文字。
  • 自定义怪物。
  • 锋利lv.9999的钻石剑。
  • 自定义刷怪笼。
  • 可以让玩家穿过的“幽灵方块”。
  • 自定义村民。
  • 自定义装备属性。
——打开任何一个我的世界的交流社区,你见到的最多的问题无外乎以上几个。(现在好点了,以前这是到处都是。)

所有这些东西,使用我们今天的教程——你都可以明白如何制作。
我们使用的技术,叫做“NBT”
我在这里保证只要你能从头到尾读完就一定能明白NBT怎么用。
我就不信不能用大白话讲清楚NBT了——
——如果你看完玩本帖还是不明白,我就去吃【哔——】。停停停,不能插旗,毕竟万事有特例。
而且,NBT不是什么特别难的东西——我完全不用发此毒誓嘛。


但先不要着急。在说明NBT之前,我们得先了解,这玩意可以用在什么上面。
所以,在开始前,请容我花费较少的篇幅介绍一条指令——

1. 召唤指令

你知道么,我这人是很少用刷怪蛋的,因为我早已熟悉使用/summon指令来生成实体

本指令可以在确定的位置,生成一个实体,并可指定NBT标签:
格式:
  1. /summon <实体ID> [<x> <y> <z>]
复制代码
(指令/summon格式,简化版)

如果你有好好看系列之前的内容,本指令的含义不言而喻:
只要将"<实体ID>"替换为一个实体,即可在后方的坐标处将其召唤。
比如,烈焰人的ID是“blaze”,所以:

例:
  1. /summon blaze 10 5 -412
复制代码
在坐标(10, 5 , -412)处召唤一只烈焰人。

当然,我告诉你烈焰人的ID是blaze,那么如何找到你想要的ID呢?
比如,僵尸的。
各位可以点此链接前往Wiki这里来查询所有实体的ID:


不过嘛,我非常好心地把这些ID列了出来。
为避免篇幅太长,这些ID被折叠了起来:
看看这些实体吧……你知道画和物品展示框实际上是实体而非方块吗?



你可能注意到了位置是个可选的元素。当你不指定坐标时,这个实体会被召唤在指令的执行地:
  1. /summon tnt
复制代码
在执行者的位置召唤一个已激活的TNT
(呵呵,你得小心了)

同理,“zombie”是僵尸:
  1. /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的格式。

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游戏刻)

我们只是把上面的中文换成了特定的英文罢了。

——以上面的“
转变成溺尸之前的剩余时间”为例——
  1. 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.8f
Fire:100s
IsBaby:1b

啊?
什么鬼。
哪三个字母“f”,“b”,“s”是什什么意思?
为什么要在特定的标签后添加这3个字母?为什么前面的“DrownedConversionTime:200”后面就什么都不用添加?【3】
这些字母是“数据类型(Data Type)”。

2.2 数据类型

什么是加在数据后面的字母?
这种加在数字后面的字母你应该不陌生。
比如——
400L
——400升

我的意思是,那些数字后的字母,有点像是这个数字的“单位”。
当然,这完全不是一种东西。
它们是“数据类型”。
什么是数据类型?

容我这么解释:

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

比如说,“是否是小僵尸”这个标签只有两种可能——要么是要么不是。我们只需要一个很小的空间就能把它存进去——因为只有两种可能,0或1。
但,像是“着火时间”这个标签所需要的电脑空间就要大得多,因为我们要存储可能的大量不同的时间值,而不仅仅是“0或1”。
但由于“着火时间”这个标签的单位是游戏的最小时间单位“游戏刻”,所以,是不可能出现小数的。
这一点就和“生命值”不同。虽然我们无法在血条上看到小数的生命值,但是只要你对游戏有一定的理解就知道生命值是有小数的。
这里就又是一个不同之处。“着火时间”是不会有小数的,我们为它分配的存储空间也不需要担心小数的问题,可以节省很多空间。
但是“生命值”是有小数的,我们需要一个不同类型的空间来存储这个数值。
以上这多种不同的存储空间,就被称为“数据类型(Data Type)

上方在数字后面的字母,就代表了,该标签使用的是什么样的数据类型
我们共有7种不同的数据类型,用于存储不同种类,不同大小的数据,均列在下表:

图标英文 学名
白话 格式 储值范围 备注
byte.png Byte 字节型只能存非常小的整数 <数字>b  -128~127 上面的“IsBaby:1b”后方的“1b”就是本类型
short.png Short 短整型 能存较小的整数 <数字>s -32768~32767 上面的“Fire:100s”中的“100s”就是本类型
Int.png Int 整型 存储整数 <数字>
不需要添加字母后缀
-2147483648~
2147483647
上面的“DrownedConversionTime:200”中的200就是本类型。注意这种类型没有字母后缀。
long.png Long 长整型 存很大的整数 <数字>l

-263~2ˆ63-1 本类型与上面的Int一样用于存储整数,但是范围要大得多。
f.png Float 单精度浮点型 小数 <数字>f 根据数据精度而定,最大为3.4*1038
上面的“Health:16.8f”中的“16.8f”就是本类型。
d.png Double 双精度浮点型 范围更大的小数 <数字>d 根据数据精度而定,最大为1.8*10308 本类型与上面的Float一样用于存储小数,但是范围要大得多。
string.png String 字符串 一串文字 "字符"'字符'
单双引号均可。
65,535个字节(大部分中文占用3个字节) 存储一串文字。有时会使用JSON文本。


就这样。很简单。
生命值是"单精度浮点型",或者说“小数”,所以我们要在后面加上“f”:
(先不要管该如何知道一个标签的数据类型是什么的问题,这个我们后面说。)
剩下的3个标签同理:

Health:16.8f
Fire:100s
IsBaby:1b
DrownedConversionTime:200

恭喜!
你终于得到了真正的标签。
上面的4对标签正确地描述了一只僵尸的生命值,燃烧事件,小僵尸,以及转化溺尸的时间。
但这里注意,冒号“:”是英文的冒号,千万别打错成中文的。
还有,NBT是大小写敏感的,别看错大小写字母,但是数据类型的后缀是不需要区分的。IsBaby:1b与IsBaby:1B都可以。


接下来,我们要做的就是把它们安进命令里就好了:
让我们看向,拥有NBT时的/summon指令格式:

  1. /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.8fFire:100sIsBaby:1bDrownedConversionTime:200}

最后用英文的逗号”,“将不同的标签隔开:

{Health:16.8f, Fire:100s, IsBaby:1b, DrownedConversionTime:200}

完成。
你得到了一个真正的NBT!(而不是刚才的”真正的标签“)
简单地把上面的东西按到命令中——

  1. /summon zombie ~ ~ ~ {Health:16.8f, Fire:100s, IsBaby:1b, DrownedConversionTime:200}
复制代码
在原地召唤一只生命值16.8,会继续着5秒的火,并会在10秒后变成溺尸的小僵尸。

有一件要注意的事情是,在NBT这对”{}“中是无视空格的。所以我在每一个“,”后都加了一个空格,为了便于阅读。
还有,NBT的顺序是完全无所谓的,哪一个放前面都行。
就这样。


同样,我们可以生成一只无敌而且有手臂的盔甲架。
ShowArms”是是否拥有可以持有东西的手臂,是字节型“Byte”。
Invulnerable”是是否无敌。同样是字节型“Byte”。
所以,根据上面的例子举一反三——
  1. /summon armor_stand ~ ~ ~ {Invulnerable:1b,ShowArms:1b}
复制代码
生成一个无敌且有手的盔甲架。


2.4 复合内的复合标签


你可能知道有一条命令叫做“/give”,其作用是给予玩家物品:
  1. /give <目标> <物品ID> [<数量>]
复制代码


非常简单,“<目标>”应是一位在线玩家的ID或选择器。物品ID与在第9帖讲过的方块ID是一个东西,只要按下F3+H打开更详细的提示即可查看(详见第系列第十帖
数量为该物品的数量,不输入默认为1个:
例:
  1. /give @a stone 10
复制代码
给与所有玩家10颗石头。

  1. /give Dahesor diamond
复制代码
给予Dahesor(我)1颗钻石。

很好理解,不多说。
各位,本指令也是可以添加NBT的。
但是你可能会发现,命令格式中并没有NBT的位置。
在本命令中,NBT应被添加到物品ID的后面,没有空格,因为这相当于是一个元素:
  1. /give 目标 物品ID{NBT} 数量
复制代码

比如,弩有一个标签Byte类型的标签“Charged”用于表示弩有没有上弦。
根据1为“真”,0为“假”的规律,我们就很容易得到:

  1. /give @a Dahesor minecraft:crossbow{Charged:1b} 1
复制代码
给予所有玩家一个上弦的弩(虽然射不出来,因为没有指定装填了什么。)

好,你应该理解了。


接下来,你可能知道,掉落物也是一种实体,其实体ID为“item”。
我们也是可以用上面说过的/summon指令在某处召唤一个掉落物的。

但这很奇怪,因为“item”只是物品的意思,并没有说“这是什么物品”。

这是因为,“是什么物品”这项信息被包含在了它的NBT"Item"中。这是一个字符串的数据类型。
比如,我们要生成一个掉落的物品。什么物品呢?就是上面那个上弦的弩。
那么,它写作
Item:"crossbow"
Item为标签名“是什么物品”,而crossbow则是"弩"。
因为这是一个字符串。所以根据上面的表格,我们要把它用双引号引起来(单引号也可以)。



接下来,我们还可以在其中包含“是否上弦”这个信息,就像我们在/give指令中做的那样——

Charged:1b

我们可以生成一个“掉落在地面上的,上弦的弩”。
同理,我们也可以包含“这一坨掉落物有几个”这个信息——

Count:2b

其中,“Count”为标签名,即“数量”的意思。2b则是一个Byte类型的值,代表了有两个。

但现在问题来了。
什么物品”是“掉落的物品”这个实体的NBT。
但是“弩是否上弦”又是“物品”的NBT的一部分。
我们该怎么办?
难道是写:
/summon item ~ ~ ~ {Item:"minecraft:crossbow", Charged:1b, Count:2b}
这样?

不不不,不是不是。
这样乱了主次了。
是否上弦”,与“数量”是用来形容物品的,而不是掉落的物品这个实体的!这样直接写乱了主次。

这怎么办呢?

答案很简单——我们在这个大的复合标签里,再套一个复合标签:

{Item:{id:"minecraft:crossbow", Count:2b}}

最外侧的,被我用绿色,较大号字体标记出的,就是NBT复合标签的那对大括号。
里面的“Item:”则是标签名。
其冒号后面的内容就是它的值。但是在这里我们无法简单地使用一个数字来表示它的值,
所以这里,值是又一个复合标签的大括号,就是紫色的那对。
其中包含了两个另外的两对键值对:
id:"minecraft:crossbow"  告诉我们“这是一个弩”。id就是“物品ID”的意思。
Count:2b  告诉我们,“弩有两个”。
这样就对了。idCount分别是两个不同的标签,但是两者整个又是Item的值,也叫子标签

你可想象成,在文件夹里夹另一个文件夹。

所以:
  1. /summon item ~ ~ ~ {Item:{id:"minecraft:crossbow", Count:2b}}
复制代码
召唤一个掉落物,为两个弩。

欸欸,等等,我们不是说好了要包含“是否上弦”这个信息的么?
那,各位,怎么加入这个NBT?
这样?:

{Item:{id:"minecraft:crossbow", Count:2b, Charged:1b}}

哦不不不不。这样主次又乱了
我们的“是否上弦”是用来形容“弩”的!
如果像上面那样写,就变成是形容物品的啦。
仔细看看就能明白,以下三个信息根本就不是并列的:
  • 物品为弩。
  • 数量两个。
  • 弩上弦了。

那怎么办呢?
简单,我们在这个被套进复合标签复合标签中,再套一个复合标签

{Item:{id:"minecraft:crossbow", Count:2b, tag:{Charged:1b}}}

请注意我用字体大小表示出的主次关系。
我们新增的标签tag: ,即“标签”,描述了本物品包含的标签。
物品包含了什么标签呢?

Charged:1b
弩已上弦。

所以:
  1. /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]}

注意在列表里也是要加上数据类型的字母后缀的。
很容易得到——

  1. /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。

所以——

  1. /give @a diamond_sword{Enchantments:[{id:"sharpness", lvl: 67},{id:"knockback", lvl: 4}]}
复制代码
给予所有玩家一把锋利Lv.67,击退IV的钻石剑(diamond_sword)。

我们只是添加了两个附魔,当然还可以再列表中加入更多。但这里就不举例了。
(附魔ID请见附录。)

这样,你理解了么?

当然上面的例子不是最难的。
我们只是在一个列表里套了两个复合标签。
但实际上,我们还可以在列表中的复合标签中套另一个列表,里面再套复合标签……

咳咳,不折磨你们了。


2.6 数列


有一个有趣的事情,就是如果列表内装的数据类型是"Int(整数)","Byte(贼小的数(字节型))",或"Long(贼大的数(长整型))",我们的列表会有变化。

因为某种代码上的,跟我们无关的原因,这时我们需要在最前面加上一个东西:

example: [B; 1b, 2b, 4b, 6b, 3b, 14b, 3b, 4b, 5b, 7b]

B;代表了整个列表都是Byte。
……但是里面的东西你一样要加上“b”用来代表byte的数据类型。

同样,我们还可以有“I; ”或“L; ”,分别为“Int(整数)”与“Long(长整型)”。
注意这些前缀是大写的,别弄错了。


这些东西被称作“数列”(Array)
而根据其列的数值的不同(最前面的字母不同),分为I; 整型数列(int Array)B; 字节型数列(byte Array),与L; 长整型数列(long Array)

这玩意是比较少见的,一般最常用的就是UUID吧。


2.7 查看


教程的最后,让我们解决最根本的问题——

我该如何知道一个NBT叫啥名,该填什么值,又是什么数据类型?
简单简单。遇事不决看Wiki。

nbt234.png


图中所示的是僵尸页面的“实体数据”段落。
在这里,你可以看到所有的标签,以及他们的类型

不就是Byte? 我们再上面的列表内也有相似的图标。


但是要补充几个:


com.png 意味着这是一个复合标签。
list.png 为列表。
byte_array.png 是Byte数列。
int_array.png 是Int数列。
long_array.png 是Long数列。
就这些。
只要你看着上面的列表,你就能很容易给出我们在最开始举的那个僵尸的例子。
不过嘛,本教程刚刚才只是讲述了NBT最基础的格式——
接下来,我会举上几个例子来帮助你详细理解~

3. 示例

在开始看实例之前,我需要让你认清楚一件事——
你比不上命令生成器。
你可能知道,有一些可以用来生成NBT命令的网站,非常好用(虽然经常更新不及时。)
我想告诉你,对于复杂一些的NBT,人肯定是没有机器快的。使用这些生成器可以让你在很短的时间里,用可视化的方法快速生成指令。
所以,你看过本帖对NBT有了解之后,请记住善用这些生成器吧。
但是你要懂其中的原理——
不然对于生成器不全的,更新不及时的,或者出BUG的地方,你就无能为力了。

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

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

我们在系列教程第十帖讲过了用于放置方块的命令/setblock与方块状态(没看过的请回去看)。
当时我们说到,对于方块实体,方块状态无法涵盖所有内容。

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

chestNBT.png

你看到的是箱子的NBT结构。你可以发现除了我们要的“ Items:当前容器内物品的列表。”外,还有诸如“上锁”之类的神奇玩意。
但我们今天不管这些。我们只要在第3格放着32个面包,第5格放着16颗石头就好。

看向上图,我们可以发现用于存储箱子物品的NBT“Item”是一个由数个复合标签组成的列表,每一个复合标签代表了一个栏位的物品。
而包含在每个复合标签中的,用于存储物品信息的“物品共通标签”实际上我们已经讲过了,就是上面的“两个弩”的掉落物的那个,是一样的。
我们可以看到字段“id”存储了该物品是什么,标签“Count”存储了物品数量,而标签“Slot”则是该物品放在箱子的哪一格。

这里注意Slot:1b并不是箱子的第一格,而是第二格,因为栏位是从0开始算的。
所以依此类推,第3格是Slot:2b,第五格是Slot:4b

所以:

{Items:[{Slot:2b, id:"bread", Count:32b}, {Slot:4b, id:"stone", Count:16b}}

这就是我们要的NBT。
上面“bread”是面包,而“stone”是石头。
我们可以直观地看到,每一个物品都是一个复合标签,而它们都被装在一个列表里。


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

  1. /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”的属性)。
首先,让我们看向自定义名称:

display.png

我给的图片是镜像Wiki的,其数据类型的图标与刚才说的有区别,但没关系,它们大体相似。

我们可以看见“控制物品的自定义显示信息”的是一个复合标签,其中可以拥有3个子标签:
字符串Name”,用于控制物品的名字字符串Lore”,用于控制物品的描述;与最后的整型Int "color",这个是控制皮革铠甲的颜色的。在本例中用不上。

这个文字要求是JSON文本,由于我们还没说到JSON,所以这里我们就用它的无特殊格式简写,及用双引号引起来。
注意NBT本身还有一个用来表示这是个字符串的单引号,所以结果是我们要把文字用“'"”,即外侧单引号加上内侧的双引号。

我们不计划在本物品中添加自定义描述,所以"Lore"我们不填。
我们想要的自定义名称是“超级无敌宇宙最强太古霸王剑”——
所以,如果你看懂了本教程,应该能明白——

diamond_sword{display:{Name:'"超级无敌宇宙最强太古霸王剑"'}}

描述了一个名称为“超级无敌宇宙最强太古霸王剑”的钻石剑(diamond_sword)。其中,由于自定义名称是字符串,所以我们用了引号""来引起来。Name是“自定义名称”的标签名,而它又是display的子标签。

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

这里我们要用的是“属性修饰器”(AttributeModifiers)来更改属性。
这里要注意的是,属性修饰器这个东西不止你在用,游戏也在用。
比如,当你疾跑时获得的速度加成实际上就是属性修饰器的一种。

我们可以在Wiki的Player.dat格式#属性修饰符页面找到它的NBT格式:

ing.gil.png

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

  • 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 就是我们属性修饰符的根标签,它是一个列表,可以包含数个属性修饰符,而每一个修饰符有以下标签:

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这里。(不用担心,随便填撞见它们的概率微乎其微……)

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


{AttributeModifiers:[{AttributeName:"generic.attack_damage",Slot:"mainhand",Amount:64d,Operation:0,UUID:[I;1,2,3,4]}]}


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

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


{AttributeModifiers:[{AttributeName:"generic.attack_damage",Slot:"mainhand",Amount:64d,Operation:0,UUID:[I;1,2,3,4]}, display:{Name:'"超级无敌宇宙最强太古霸王剑"'}]}



加入指令头,目标等,我们可以得到最终的命令:
  1. /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







注释

【1】本帖内不区分执行者与执行地
【2】本标签一旦大于0,溺尸的转变就是不可逆的,离开水也没用。
【3】理论上,在一些情况中,即使你不在数字后添加用于确定数据类型的字母,系统也会自动补全。有些标签甚至接受不同类型的输入。但是!不填字母容易出BUG。所以,请总是填入字母。



所以就这样吧。
最后,各位,给点人气(盛满怨念)
以上。


#更新日志:
Java 1.16.5/a 页面发布
Java 1.16.5/b 感谢的提醒,修改了一处错误
Java 1.16.5/c 添加了1个示例
Java 1.16.5/d 更改了关于JSON的错误。
Java 1.16.5/e 添加了新示例。
Java 1.16.5/f  感谢@SPGoding 的纠错,改正了关于数列的很严重的错误。

Java 1.17/a 1.17版本升级完成


评分

参与人数 8人气 +15 金粒 +40 收起 理由
BlackCB. + 3 MCBBS有你更精彩~
ykyh836657876 + 1 MCBBS有你更精彩~
SPGoding + 2 + 30 MCBBS有你更精彩~
振翮高飞 + 2 MCBBS有你更精彩~
磨砺 + 2 MCBBS有你更精彩~
mGHLy + 1 + 10 神乎其技!6的飞起!
玄素 + 3 好好睡觉
璀璨星河丶 + 1 MCBBS有你更精彩~

查看全部评分

mGHLy 当前离线
积分
1079
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2020-7-21
查看详细资料
发表于 2021-4-26 17:34:39 | 显示全部楼层
{小盆友的糖:[1, 2, 3, 4, 5}
这里少了个中括号~

楼主注意身体啊,不要天天熬夜肝教程了
今天人气用完了,明天补给你

评分

参与人数 1人气 +2 收起 理由
Dahesor + 2 已修复,感谢

查看全部评分

回复

使用道具 举报

(=°ω°)丿 当前离线
积分
6377
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2017-8-25
查看详细资料
发表于 2021-4-30 20:17:31 | 显示全部楼层
还有另一种理解:
键值对 > 文件;
键名 > 文件名;
键值 > 文件里的内容;
数据类型 > 文件的后缀名(如 png、json、mcfunction);
复合标签 > 文件夹(里面可以放其他文件);
nbt 路径 > .minecraft\saves\<地图名>\datapacks。

评分

参与人数 1人气 +2 金粒 +30 收起 理由
Dahesor + 2 + 30 感谢,已更新(但没更完待补充).

查看全部评分

回复

使用道具 举报

Dahesor 当前离线
积分
2079
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-2-5
查看详细资料
 楼主| 发表于 2021-5-1 01:59:41 | 显示全部楼层
本帖最后由 Dahesor 于 2021-5-1 02:06 编辑
(=°ω°)丿 发表于 2021-4-30 20:17
还有另一种理解:
键值对 > 文件;
键名 > 文件名;

这个可以,感觉可以在下一帖说/data的时候用上……但这一帖……
啧啧,等我研究研究

谢谢哈,从来没见过这种理解法
回复

使用道具 举报

YYTMI 当前离线
积分
25
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2021-3-3
查看详细资料
发表于 2021-5-1 11:58:37 | 显示全部楼层
有点看不懂

评分

参与人数 1人气 -1 金粒 -10 收起 理由
SHEEP_REALMS -1 -10 请仔细阅读版规,本版严禁灌水!.

查看全部评分

回复

使用道具 举报

Dahesor 当前离线
积分
2079
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-2-5
查看详细资料
 楼主| 发表于 2021-5-1 12:26:40 | 显示全部楼层

1. 如果你有仔细看,请务必告诉我哪里没写清楚(毕竟我写的时候迷迷糊糊)
2. 如果没仔细看,请仔细看。
谢谢。
回复

使用道具 举报

Dahesor 当前离线
积分
2079
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-2-5
查看详细资料
 楼主| 发表于 2021-5-1 12:27:02 | 显示全部楼层
本帖最后由 Dahesor 于 2021-5-3 11:33 编辑
\(不知道为什么好像网卡了发了两个回复,莫怪谢谢。)
1. 如果你有仔细看,请务必告诉我哪里没写清楚(毕竟我写的时候迷迷糊糊)
2. 如果没仔细看,请仔细看。
谢谢。
回复

使用道具 举报

jjxxz2 当前离线
积分
6024
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2012-8-3
查看详细资料
发表于 2021-5-1 13:13:07 | 显示全部楼层
而根据其列的数值的不同(最前面的字母不同),分为I; 整型数列(int Array),B; 字符串数列(byte Array),与L; 长整型数列(long Array)。

字符串应该是打错了

评分

参与人数 1人气 +1 收起 理由
Dahesor + 1 感谢,已修正

查看全部评分

回复

使用道具 举报

xiaodan_979791 当前离线
积分
414
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-7-17
查看详细资料
发表于 2021-5-1 15:07:50 | 显示全部楼层
打了多久的字
回复

使用道具 举报

早起的鸟有虫吃 当前离线
积分
33
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2021-5-1
查看详细资料
发表于 2021-5-1 18:35:40 | 显示全部楼层
学习学习完成任务

评分

参与人数 1人气 -1 金粒 -10 收起 理由
SHEEP_REALMS -1 -10 请仔细阅读版规,本版严禁灌水!.

查看全部评分

回复

使用道具 举报

7894561231945 当前离线
积分
83
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-7-2
查看详细资料
发表于 2021-5-5 18:52:44 | 显示全部楼层
23333333333

评分

参与人数 1人气 -1 金粒 -10 收起 理由
SHEEP_REALMS -1 -10 请仔细阅读版规,本版严禁灌水!.

查看全部评分

回复

使用道具 举报

7894561231946 当前离线
积分
41
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2021-5-5
查看详细资料
发表于 2021-5-5 20:15:35 | 显示全部楼层
23333333333

评分

参与人数 1人气 -1 金粒 -10 收起 理由
SHEEP_REALMS -1 -10 请仔细阅读版规,本版严禁灌水!.

查看全部评分

回复

使用道具 举报

SPGoding 当前离线
积分
22609
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2018-1-18
查看详细资料
发表于 2021-5-7 01:29:22 | 显示全部楼层
id与Counnt分别是两个不同的标签

多了个 n

{小盆友的糖:[1, 2, 3, 4, 5}

丢了个 ]

example: [B; 1, 2, 4, 6, 3, 14, 3, 4, 5, 7]

B; 表示的只是数列的类型,里面的各个数字还是要带上 b 后缀的,不然相当于把 Int 塞到了 Byte 数列里,会报错的。

以及似乎可以在哪里提一句表示数据类型的后缀(bsLfd)是不区分大小写的(然而表示数列类型的 BIL 都要大写),只是 MC 在输出 SNBT 数据的时候会都显示成小写(L 除外,估计是为了和数字 1 区分)

评分

参与人数 1人气 +2 收起 理由
Dahesor + 2 MCBBS有你更精彩~

查看全部评分

回复

使用道具 举报

Dahesor 当前离线
积分
2079
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-2-5
查看详细资料
 楼主| 发表于 2021-5-7 01:57:04 | 显示全部楼层

感谢,已改正。

数列真是很少用啊,现在一想我也只用在UUID而已。
还真不知道这件事。

谢谢。

评分

参与人数 1人气 +1 收起 理由
SPGoding + 1 睡觉!

查看全部评分

回复

使用道具 举报

asd1551614597 当前离线
积分
74
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2019-7-3
查看详细资料
发表于 2021-5-9 18:45:15 | 显示全部楼层
66666666666666666

评分

参与人数 1人气 -1 金粒 -10 收起 理由
SHEEP_REALMS -1 -10 请仔细阅读版规,本版严禁灌水!.

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册(register)

本版积分规则

Archiver|小黑屋|Mcbbs.net ( 京ICP备15023768号-1 ) | 京公网安备 11010502037624号 | 手机版

GMT+8, 2021-9-21 04:26 , Processed in 0.089908 second(s), Total 41, Slave 34 queries, Release: Build.2021.09.13 2028, Gzip On, Redis On.

"Minecraft"以及"我的世界"为Mojang Synergies AB的商标 本站与Mojang以及微软公司没有从属关系

© 2010-2020 我的世界中文论坛 版权所有 本站原创图文内容版权属于原创作者,未经许可不得转载

快速回复 返回顶部 返回列表