第一百五十九章 深入了解NBT树和NBT路径
在上一章,你应该已经理解了什么叫作NBT树,但理解不代表会运用。要真正搞懂NBT树的理念,我们得投入实践,学会阅读NBT树甚至表示出一个NBT树。
NBT数据树常常会表示成下面这样的格式:
<数据类型>[root或其他具体名称]:根、父级标签或其他具体的名称或描述
│
├<数据类型>[名称]:值或描述
......
└<数据类型>[名称]:值或描述
注:在上面的格式中,<>代表必定会出现,[]代表可能会出现(含义跟指令格式中的一样)。
许多网站和程序,比如Minecraft Wiki、常用的Java版NBT编辑器NBTExbr /lorer,都采用了上述格式来表示NBT。
举个简单的例子,在Minecraft Wiki上,Java版书与笔的物品NBT是这么表示出来的:
▦ tag:父级标签
└▤ br /ages:书与笔的各个页面。
空└✎:一个单页。每一页是一个字符串,不能超过32767个字符。
在上面的这个例子中,由于Minecraft Wiki使用一个图片标识来表示数据类型,所以这里用一些特殊符号来代替:
▦——Combr /ound复合标签
▤——List列表
✎——String字符串
仔细观察Minecraft Wiki给出的NBT树,结合我们前面所学的知识,你应该不难得出上面这个例子所表示的意思:
『对于物品书与笔,在其物品NBT标签中的tag复合标签下,有一个名为br /ages的列表型标签。这个br /ages列表是一个由多个字符串类型的值组成的列表,每个值都代表着书与笔中的一页,且每个值都不得拥有超过32767个字符。』
什么?你很难理解?或许我们需要睁大眼睛,逐行研究上面的例子。
第一行:▦ tag:父级标签
根据上面的格式『<数据类型>[root或其他具体名称]:根、父级标签或其他具体的名称或描述』,我们不难知道这在表示一个名为tag的复合标签。同时,它被解释为『父级标签』,说明Minecraft Wiki在这里给出的NBT树是在描述这个标签的孩子(也就是值)。
等等,为什么Minecraft Wiki不直接从根开始,而是从这个tag标签开始呢?
回忆一下第十一卷的内容,tag标签是物品通用标签内的一个标签,用来储存物品的额外信息。所有额外的物品标签都在tag标签内,因此Minecraft Wiki不从根开始描述的原因也就不难理解——使文章详略得当。
问题解决,我们来看看第二行:└▤ br /ages:书与笔的各个页面。
最开始的└意味着在这里,NBT数据树的这条树枝上有一个节点,而且由于└没有继续向下延伸,因此这个节点是该树枝上最后一个节点。第二个▤代表的意思很明确,即该节点对应的NBT标签是个列表。『br /ages』是该列表的标签名,而后面的『书与笔的各个页面。』是对该标签的一个描述。
第三行更加简单:└✎:一个单页。每一页是一个字符串,不能超过32767个字符。
最开始的└不用说,✎估计也不用说。但✎后面的标签名称呢?
还是那句话,这是个列表,列表是由多个相同类型的值组成,而不是标签。换句话说,这部分NBT树实际上描述的不是个标签,而是个值,自然也就不需要给出名称,因为没有名称。
冒号右边的很明显,是个对值的描述,相信大家都看得懂这里的中文,我就不再阐述。
现在,你肯定已经拥有阅读Minecraft Wiki上NBT树状图的基本素养。来尝试一下下面的这个NBT树:
▦实体数据值
│实体共通标签
│活体共通标签*
│生物共通标签
├🄸 Size:史莱姆的大小。最小为0,即小型史莱姆大小;最大值为126,超过126的值将当作126处理。
└◧ wasOnGround:表示史莱姆是否正在接触地面。
图示:
🄸—— Int整型
◧—— Boolean布尔值(Byte字节型)
*Minecraft WIki把生物共通标签给拆开了?啥时候多出个活体共通标签?
上面唯一需要注意的一点是,你应该知道Byte字节型有两个种类的值,一个是普通的数值,一个是布尔值。为方便区分,Minecraft Wiki将布尔值类型从Byte字节型中独立出来,成为Boolean布尔值,但实际上在NBT中还是Byte字节型。
这里就暂时不放参考答案(实际上是因为懒得写),相信大家都能够看懂上面的NBT树状图。
搞懂NBT树状图后,接下来让我们继续了解NBT路径。
在上一章,我们基本上搞清楚了NBT路径是什么。比如对于下面这个NBT路径:
Abr /br /le[0].Cen.Sama
你应该知道它有三个意思:
①这代表Sama标签的路径,你可以通过这个路径来找到这个标签
②这代表Sama这个标签
③这代表Sama这个标签的值
而在Minecraft中,NBT路径最常见的意思就是第二和第三个——代表一个标签和或其值。或者说,NBT路径最常见的作用,就是用来寻找并获取到一个标签,然后对这个标签的值进行一些操作。
举个简单的例子,还记得你在第一百零八章掉下来的钻石吗?把它拿过来再看看:
{Age:0s,Health:5s,Pickubr /Delay:0s,Item:{Count:1b,id:“minecraft:diamond“}
↑↑↑这是你掉的钻石的NBT,需要注意这只不过是一个简化版↑↑↑
如果我们要使用/data指令来获取到这颗钻石Age标签的值,这个NBT路径该怎么写?
很简单,就一个单词:
Age
然后在聊天框中就会返回:0s
但其实你也可以这么写:
{Health:5s}.Age
这是怎么一回事?前面的{Health:5s}是咋冒出来的?
这虽然是个多此一举的写法,但我们从中也可以了解到NBT路径的一个特殊功能:匹配特定的NBT标签
有时候,我们固然想要得到一个标签的值,但当我们想把范围缩小时,比如想要获取所有钻石掉落物的Age值,我们可能就无从下手。但其实,这有两种方法:
第一种,使用目标选择器;第二种,在NBT路径内加入一些匹配标签用的值。
其实这两种方法的原理都一样,但由于目标选择器的nbt参数我们不现在讲,因此先来看看第二种方法。
首先,路径『Age』虽然是正确的,但其实在这里省略了一些东西。
什么东西?没错,根标签呢?
一般情况下,根标签都会被省略不写。如果不省略,那整个路径会变成:
{}.Age
其中,{}是根标签的路径,也就是代指根标签。由于根标签是个复合标签,所以用大括号表示。
然后,对比『{Health:5s}.Age』和『{}.Age』,我们会发现两者唯一的区别就在于根标签拥有一个值:Health:5s
很奇怪吧?明明路径就可以代表标签的值,为什么还要专门写一个特定的值呢?
其实,给路径中的一个标签加上值看上去多此一举,但实际上很有用。因为它有一个功能——过滤。
如果不加上Health:5s,那么单独的{}将代表任何一个根标签。但如果加上Health:5s,那么这仅能代表含有Health:5s这个标签的根标签。
比如{Health:5s}.Age虽然能选中上面钻石的Age值,但一定选不中下面这个钻石的Age值:
{Age:-1s,Health:32767s,Pickubr /Delay:0s,Item:{Count:1b,id:“minecraft:diamond“}
同理,对于路径『Age』,我们也可以写成这样:
Age:100s
这看起来是一个SNBT,但其实也是个路径,意思是寻找根标签内值为100s的Age标签。
总而言之,如果给NBT路径内的某个或多个标签加上一个特定的值,那就能起到过滤的作用,使结果更加精确。
你现在应该知道如何获取所有钻石掉落物的Age值吧?让我们试一试!
/execute as @e run data get entity @s {Item:{Count:1b,id:“minecraft:diamond“}}.Age
返回:
钻石拥有以下实体数据:163s
钻石拥有以下实体数据:127s
钻石拥有以下实体数据:11451s
(上面的data指令之后再解释)
GOOD!另外,你应该能解释『{Item:{Count:1b,id:“minecraft:diamond“}}.Age』的意思吧?试一试描述一下它的意思。
上面的内容只是NBT路径的冰山一角,在下一章,我们将会继续深入了解NBT路径,但至于何时更新是个大问题。