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