第一百五十八章 重新認(rèn)識NBT
看到這個題目,你是不是大吃一驚?NBT怎么還要重新認(rèn)識?
別太驚訝。還記得最開始我們接觸到NBT的時候嗎?在第五十四章的最后,我指出這其實并不是真正的NBT,而是Mojang專門搞出來給人看的『SNBT』,即『Stringified NBT』,『字符串化的二進(jìn)制命名標(biāo)簽』。在之后的章節(jié),我們也重點(diǎn)了解了SNBT,知道了它的格式和使用方法。而現(xiàn)在,我們已經(jīng)了解并掌握了許多物品和實體的NBT,這時候,也是時候來認(rèn)識一下真正的樹狀結(jié)構(gòu)的NBT,這樣子你才能真正理解接下來的內(nèi)容。
—————分割線—————
樹,是大自然的鬼斧神工。它提供了人類賴以生存的氧氣和食物,也給了人類許多靈感。
一棵樹,有樹根、樹干、樹葉。它們均發(fā)源于一點(diǎn),我們可以把這一點(diǎn)稱為——根(root)。為了方便講解,我們接下來忽略位于地底下的樹根,只看樹干和樹葉。
樹干上有許多分叉點(diǎn),我們可以把這些分叉點(diǎn)稱為——節(jié)點(diǎn)(node)。通過節(jié)點(diǎn)可以長出一些樹葉或新的樹干,在新的樹干上則又有許多新的節(jié)點(diǎn),分叉出更多的樹葉和樹干。
一個節(jié)點(diǎn)所分叉出的所有樹葉和樹干,以及這些樹干上所有的節(jié)點(diǎn),可以全部視為這個節(jié)點(diǎn)的值(value)。你可以這么理解——這些樹葉和樹干,就是這個節(jié)點(diǎn)所存在的價值。如果沒有這些樹葉和樹干,那么這個節(jié)點(diǎn)也將不復(fù)存在。
這就是一棵樹——一顆完全由根、節(jié)點(diǎn)和值構(gòu)成的樹!(圖158-1)
根其實是一種特殊的節(jié)點(diǎn),它是整個樹的開始,所以整個樹都可以看作是根的值。
除了根,其他節(jié)點(diǎn)都可以取名字。在同一個樹干上,每個節(jié)點(diǎn)的名字都必須是獨(dú)一無二的,以防與其他節(jié)點(diǎn)弄混。
比如,我們可以給根上面的第一個節(jié)點(diǎn)取名『Apple』、第二個節(jié)點(diǎn)取名『Beluga』。這樣子,我們就不容易搞混這兩個節(jié)點(diǎn),也可以方便用名稱指代這兩個節(jié)點(diǎn)。
假設(shè)第一個節(jié)點(diǎn)長出了一條樹干,上面也有一個節(jié)點(diǎn),我們給它取名為『Cen』。而這個『Cen』節(jié)點(diǎn)也長出了一條樹干,上面也是有一個節(jié)點(diǎn),我們給他取名『Sama』(圖158-2)。那么,如果其他人也要尋找『Sama』節(jié)點(diǎn),該如何尋找呢?
假設(shè)其他人只知道這個節(jié)點(diǎn)叫『Sama』然后去尋找它,在節(jié)點(diǎn)很多的情況下這無疑是大海撈針。而且有可能在不同樹干上的其他節(jié)點(diǎn)也叫『Sama』。這時候該怎么辦?
我們來看一個現(xiàn)實生活中的情景:
你此時正在學(xué)校里上課,老師要求你將書翻到這節(jié)課要講的『第三模塊第五單元第一課』。你肯定是先找到『第三模塊』,然后找到『第五單元』,最后找到『第一課』。老師不可能只告訴你『翻到第一課』,因為每個單元都有自己的第一課。老師這樣說,讓你得知了『第一課』的路徑(path),于是你才找到了這節(jié)課要講的『第一課』。
回到上面的問題,答案已經(jīng)十分明顯了——那位要尋找『Sama』節(jié)點(diǎn)的,應(yīng)該去詢問得知『Sama』節(jié)點(diǎn)具體位置的人,比如我們。然后我們應(yīng)該將『Sama』節(jié)點(diǎn)的路徑告訴他,也就是:
Apple.Cen.Sama
這就是『Sama』節(jié)點(diǎn)的路徑。如你所見,一個路徑就是『節(jié)點(diǎn).節(jié)點(diǎn).節(jié)點(diǎn)』的形式,從左往右的節(jié)點(diǎn)指出了一條從根開始通向終點(diǎn)節(jié)點(diǎn)的具體道路(圖158-3)。
路徑既然可以用來找到節(jié)點(diǎn),那么他們應(yīng)該也就能夠表示節(jié)點(diǎn),乃至于表示這個節(jié)點(diǎn)的值。畢竟『一個節(jié)點(diǎn)』只對應(yīng)『一條路徑』,『一條路徑』也只對應(yīng)『一個節(jié)點(diǎn)』,『一個節(jié)點(diǎn)』也只能有『一個值』。因此,如果我們再一次看向『Sama』節(jié)點(diǎn)的路徑『Apple.Cen.Sama』,我們就會發(fā)現(xiàn)『Apple.Cen.Sama』既可以用來表示『Sama』節(jié)點(diǎn),也可以用來表示『Sama』節(jié)點(diǎn)的值。(只不過Sama節(jié)點(diǎn)的值是空的)
又比如說,路徑『Apple.Cen』既表示節(jié)點(diǎn)『Cen』,也表示節(jié)點(diǎn)『Cen』的值——從『Cen』節(jié)點(diǎn)長出的樹干以及上面的『Sama』節(jié)點(diǎn)。
看起來像『節(jié)點(diǎn).節(jié)點(diǎn)』這樣的路徑解決了尋找節(jié)點(diǎn)以及節(jié)點(diǎn)的值的問題,但如果『Apple』節(jié)點(diǎn)又長出了一條新的樹干,這條新的樹干上又有一個節(jié)點(diǎn)『Touhou』該怎么辦(圖158-4)?此時『Sama』節(jié)點(diǎn)的路徑又該如何表示?
很明顯,現(xiàn)在『Apple』節(jié)點(diǎn)長出了兩條樹干(另外兩條是貫穿Apple節(jié)點(diǎn)的從根長出來的主干,其實是一條,不要搞錯了),我們需要有一個正確的方式來表示這兩條樹干。比如我們可以取名字,但這似乎過于麻煩了,而且容易把樹干和節(jié)點(diǎn)搞混。最好也最簡單的方法就是給樹干編『號數(shù)』,也就是使用索引(index)。
將節(jié)點(diǎn)『Cen』所在的樹干編上索引0,那條新的樹干編上1,然后我們只需要在路徑中的『Apple』后面,『.』前面加上樹干的索引,就大功告成了:
Apple[0].Cen.Sama
其中,[0]指的就是索引為0的樹干,也就是『Cen』節(jié)點(diǎn)所在的那一條。通過這種方法,我們也可以表示『Touhou』節(jié)點(diǎn):
Apple[1].Touhou
不過,這些東西跟NBT有什么關(guān)系呢?
這個問題問得好?,F(xiàn)在,讓我們再來看看另外一顆樹(圖158-5):
根
│
├id:“minecraft:jukebox“
├IsPlaying: 1b
│
├ RecordItem:
│├Count: 1b
││
│└id:“minecraft:music_disc_ward“
│
├x:-1
├y: 60
├z:-53
│
├RecordStartTick: 0L
└TickCount: 514L
仔細(xì)看這東西,你有沒有發(fā)現(xiàn)?
如果沒有發(fā)現(xiàn),那讓我們把這棵樹的形態(tài)轉(zhuǎn)化一下,變成:
{id:“minecraft:jukebox“,x:-1,y:60,z:-53, IsPlaying :1b, RecordItem :{Count:1b,id:“minecraft:music_disc_ward“}, RecordStartTick:0L, TickCount:514L}
這不就是SNBT嗎?
沒錯,上面那棵樹,其實就是下面這個SNBT的NBT形式,游戲所看到的NBT就長這個模樣。
這棵NBT其實就是一個放在(-1,60,-53)的唱片機(jī)的方塊實體。我們會在第十八卷具體了解到方塊以及方塊實體的內(nèi)容,在這邊你只需要將方塊實體當(dāng)作是方塊NBT即可。從這棵NBT中,我們和游戲都可以得知,這個唱片機(jī)此時正在播放(IsPlaying)ward唱片(RecordItem),并且已經(jīng)播放了25.7秒(TickCount),這個唱片機(jī)在此之前沒有播放過任何唱片(RecordStartTick)。
你可以在Minecraft Wiki上搜索『唱片機(jī)』來具體了解上述標(biāo)簽的作用,NBT都講到這了你應(yīng)該不可能看不懂Minecraft Wiki上關(guān)于NBT的內(nèi)容吧?
回到這棵NBT樹上,仔細(xì)觀察它與它的SNBT形式,你應(yīng)該不難發(fā)現(xiàn),id節(jié)點(diǎn)的樹葉上寫著『“minecraft:jukebox“』,x、y、z節(jié)點(diǎn)的樹葉上寫著『-1』『60』和『-53』,IsPlaying節(jié)點(diǎn)的樹葉上寫著『1b』,RecordStartTick和TickCount節(jié)點(diǎn)的樹葉上寫著『0L』和『514L』。在SNBT中,上面這些節(jié)點(diǎn)所對應(yīng)的標(biāo)簽的值的類型都是字符串、Int整型、Byte字節(jié)型和Long長整型。也就是說,String字符串、Byte字節(jié)型、Short短整型、Int整型和Long長整型,以及其他的浮點(diǎn)數(shù)數(shù)據(jù)類型,在NBT樹中都無一例外承載于『樹葉』之上。
而RecordItem節(jié)點(diǎn),長出的是一條樹干,樹干上有id和Count節(jié)點(diǎn)。在SNBT中,RecordItem標(biāo)簽的值類型是復(fù)合標(biāo)簽,復(fù)合標(biāo)簽內(nèi)也有id和Count標(biāo)簽。也就是說,『復(fù)合標(biāo)簽』在NBT樹中所體現(xiàn)出來的就是一條樹干。
也就是說,整個NBT標(biāo)簽其實就是一個復(fù)合標(biāo)簽?
沒錯,確實是這樣。
通過上面的觀察,你應(yīng)該已經(jīng)總結(jié)出來了NBT樹與SNBT的一些對應(yīng)關(guān)系,比如節(jié)點(diǎn)對應(yīng)標(biāo)簽名,樹干對應(yīng)復(fù)合標(biāo)簽。那……列表呢?在NBT樹中,列表又是長什么樣子?
還記得上面的『Apple』節(jié)點(diǎn)嗎,我們在研究節(jié)點(diǎn)的路徑(path)時,遇到了一個節(jié)點(diǎn)長出兩條樹干的情況。如果我們以SNBT的形式將『Apple』節(jié)點(diǎn)表示出來,其實就是這樣的:
{Apple:[{Cen:{Sama:}},{Touhou:}]}
在SNBT中,『Apple』標(biāo)簽的值就是一個復(fù)合標(biāo)簽列表!列表的順序規(guī)定了列表內(nèi)每個元素(element)的索引值(index)。和生活中數(shù)數(shù)不一樣的是,列表的索引并不是從1開始,而是從0開始,這就是為什么我們給『Cen』節(jié)點(diǎn)所在的樹干標(biāo)上索引0而不是1的原因。
同理,如果一個節(jié)點(diǎn)長出了一堆承載相同類型數(shù)據(jù)的葉子,那這個節(jié)點(diǎn)的值也是一個列表(或數(shù)組)。如果一堆值是列表的節(jié)點(diǎn)擠在一起,那這一堆節(jié)點(diǎn)所形成的大節(jié)點(diǎn)自然也是一個值是列表的節(jié)點(diǎn),而且這列表還是列表的列表:
{節(jié)點(diǎn):[[{},{},…],[{},{},…],…]}
\\它的SNBT形式\\
只不過,像這樣的二維列表,甚至是三維列表,我們在之前都沒有碰到過,在以后也很有可能不會碰到。
這就是NBT的樹狀結(jié)構(gòu),也就是真正的NBT??雌饋碇v了很多,但其實內(nèi)容并不是很難,唯一的難點(diǎn)估計是語言比較枯燥。
但這并不是本章唯一的重點(diǎn)。你應(yīng)該還記得剛才講的『節(jié)點(diǎn)的路徑』。相比于NBT的樹狀結(jié)構(gòu),我們會在接下來的內(nèi)容中更常碰見節(jié)點(diǎn)的路徑(path)。其實,這個『節(jié)點(diǎn)的路徑』,就是NBT路徑(NBT path)——用來從NBT數(shù)據(jù)樹中指定一系列特定元素的描述性標(biāo)簽。
在接下來的/data指令中,NBT路徑是非常重要的一個東西,可以說只要你掌握了NBT路徑,你就幾乎掌握了/data指令。我們會在下一章更加深入了解NBT路徑,但在此之前,請你『務(wù)實一點(diǎn),把NBT的戰(zhàn)術(shù)打法,NBT樹的這個理念先搞懂』——范志毅,國家Minecraft指令教學(xué)前任大將軍,此句話為他2013年6月15日在評價Minecraft指令教學(xué)時所說的名言警句。
本章到此為止。
對了,2023新年快樂!