創(chuàng)造自己的世界——Minecraft 1.19的地形生成(三)
在上一篇專欄中我們介紹了世界的噪聲生成、礦脈等。這些操作都在區(qū)塊狀態(tài) NOISE 中執(zhí)行。這篇文章將繼續(xù)介紹世界生成接下來的階段。
一. 地表生成
在 NOISE 階段后,區(qū)塊會進(jìn)入 SURFACE 階段,也就是生成地表。雖然它叫“地表生成”,它實(shí)際上控制的不止地表。下面這些都是由地表生成器生成的:
深板巖層和基巖層。
惡地陶瓦條帶層與巖柱。
凍洋的巨型冰山和冰蓋。
裸巖山峰上的方解石條帶,尖峭山峰和冰封山峰等的細(xì)雪條帶。
所有地表的草方塊-泥土層、沙子-砂巖層,海底的沙礫、沙子層等。
世界的大部分地形特征基本都由地表生成器決定。地表生成器會對世界中已存在的所有基礎(chǔ)方塊位置進(jìn)行計(jì)算,通過數(shù)據(jù)包的地表規(guī)則(surface_rule
)決定一個(gè)位置上的方塊最終會變?yōu)槭裁礃幼?。下面以主世界的地表?guī)則的一部分進(jìn)行舉例:
可以看到在第一層嵌套中地表規(guī)則只有這三項(xiàng):
生成基巖層。
生成普通地表。
生成深板巖層。
地表生成器會根據(jù)定義的順序挨個(gè)嘗試執(zhí)行,如果執(zhí)行成功就不再執(zhí)行接下來的規(guī)則。假如一個(gè)方塊位置在 Y = -64,那么它肯定會變?yōu)榛鶐r,而不會計(jì)算接下來的地表規(guī)則。根據(jù)上面的代碼來看,基巖層是優(yōu)先度最高的,防止出現(xiàn)基巖空洞;而深板巖層是最后計(jì)算的,即它不能覆蓋已生成的地表,表現(xiàn)上來看就是即使在很深的露天位置會生成地表而不是深板巖。
普通地表的生成也有它的“深度”,也就是說地表的特征不能覆蓋到太深層次的地下。下面為普通地表的 JSON 設(shè)置:
普通地表的深度受 above_preliminary_surface 限制,當(dāng)它通過時(shí)地表才能生成,如果不通過則不能生成。它與密度函數(shù) initial_density_without_jaggedness(不帶粗糙度的初始密度)有關(guān),以 0.390625 為界,向下偏移 2~8 格就是這個(gè)條件的邊界。通常來說初始密度的界限非常接近實(shí)際生成的空氣-地表邊界,或比地表稍微向下一點(diǎn),這樣能確保地表能生成,但也影響了一些表層洞穴可能會生成地表。
有關(guān)于這個(gè) JSON 的具體代碼和解釋可以在 wiki 找到。
除了 JSON 定義的地表規(guī)則外,有一些地表生成是硬編碼的。風(fēng)蝕惡地的巖柱、凍洋的巨型冰山都不受地表規(guī)則影響,它們都由地表生成器直接生成,而巖柱早于地表規(guī)則放置,巨型冰山晚于地表規(guī)則放置。
二. 地形雕刻
在地表生成后,區(qū)塊進(jìn)入 CARVERS 階段,開始進(jìn)行地形雕刻。雕刻是為了細(xì)化地形,并生成雕刻器洞穴,用于將 NOISE 階段的噪聲洞穴更好的聯(lián)系起來。
目前 MC 中只有三種雕刻器:洞穴雕刻器、峽谷雕刻器和下界洞穴雕刻器。洞穴雕刻器生成了雕刻器洞穴,包括管道狀的洞穴和環(huán)形內(nèi)庭;峽谷雕刻器會生成巨大的峽谷。
雕刻器洞穴和噪聲洞穴雖然都是洞穴,但是它的規(guī)模和數(shù)量差別巨大。區(qū)分這兩種洞穴的辦法就是通過 F3 屏幕上的 N(最終密度值),如果 N 大于 0 則為雕刻器洞穴,N 小于 0 則為噪聲洞穴。
由于地形雕刻沒有什么好說的,所以這篇文章不會講它的算法。
在 CARVERS 階段后的下一個(gè)階段 LIQUID_CARVERS 在 1.18-pre3 時(shí)被清除了操作,因?yàn)檫@一階段是液體雕刻,而液體雕刻階段的水下洞穴和水下峽谷在 21w06a 就被移除了,所以這個(gè)階段在現(xiàn)在的版本已經(jīng)沒有意義。
三. 地物生成
在地形雕刻之后,就來到了世界生成的重點(diǎn)環(huán)節(jié):地物生成(FEATURES)。
地物生成階段會生成世界上所有的地物,無論是花草樹木還是地下的礦石都在這一階段產(chǎn)生。結(jié)構(gòu)的實(shí)際放置也會在這個(gè)階段進(jìn)行,通過讀取第一、二階段的數(shù)據(jù)得出結(jié)構(gòu)位置進(jìn)行放置。
地物不是在一起生成的,而是再細(xì)分為幾個(gè)階段生成:
這 11 個(gè)階段中,除了 STRONGHOLDS 階段沒有任何作用外,其他階段都有各自的作用:
RAW_GENERATION,用于生成其他地物依賴的位置。原版中這里只有一個(gè)末地小島的地物。
LAKES,生成湖。在 1.18 之后水湖已經(jīng)被含水層替代,目前這里只剩下熔巖湖地物。
LOCAL_MODIFICATIONS,生成一些對地形影響較大的地物。原版中紫晶洞、冰山等會在這個(gè)階段生成。
UNDERGROUND_STRUCTURES,生成地下的結(jié)構(gòu)。原版中廢棄礦井、化石、埋藏的寶藏在這個(gè)階段生成。
SURFACE_STRUCTURES,生成地表結(jié)構(gòu)。大部分結(jié)構(gòu)都在這個(gè)階段生成,沙漠水井和冰刺也是在這個(gè)階段生成。
STRONGHOLDS,無作用。要塞在地表結(jié)構(gòu)生成時(shí)生成。
UNDERGROUND_ORES,生成地下礦物。所有的團(tuán)簇都在這個(gè)階段生成。
UNDERGROUND_DECORATION,生成地下裝飾。遠(yuǎn)古城市在這個(gè)階段生成,幽匿斑塊也是這個(gè)階段生成的。
FLUID_SPRINGS,生成涌泉。
VEGETAL_DECORATION,生成植物裝飾。樹木、花草斑塊等都是在這個(gè)階段生成。
TOP_LAYER_MODIFICATION,頂層修改。這個(gè)階段只有冰凍頂層地物,用于生成冰雪表面。
在每個(gè)具體的階段,結(jié)構(gòu)都優(yōu)先于地物生成。結(jié)構(gòu)的生成順序是固定的,按照字符串的順序依次放置;地物放置順序也是固定的。具體地物階段列表可以看 https://minecraft.fandom.com/zh/wiki/User:Nickid2018/%E4%B8%96%E7%95%8C%E7%94%9F%E6%88%90%E8%A1%A8。
先說結(jié)構(gòu)放置。結(jié)構(gòu)的生成點(diǎn)在區(qū)塊生成的前兩個(gè)階段就已經(jīng)產(chǎn)生,并且包含了結(jié)構(gòu)片段的起始點(diǎn)和參數(shù),在結(jié)構(gòu)真正生成時(shí)只會執(zhí)行當(dāng)前區(qū)塊內(nèi)含有的所有結(jié)構(gòu)片段的 postProcess 方法。如果結(jié)構(gòu)片段超出了本區(qū)塊會怎么樣呢?答案是超出的部分也會成功放置。地物生成階段和其他階段的不同在于,這個(gè)階段修改區(qū)塊是可以超出本區(qū)塊范圍的,但是不能超過以本區(qū)塊為中心的 3x3 區(qū)塊。如果一個(gè)結(jié)構(gòu)片段或地物生成超出了這個(gè)限制,那么超出部分不能被成功放置,看起來就像是被切斷了一樣。
簡單介紹了結(jié)構(gòu)的放置,接下來來說說地物的。地物的放置每個(gè)區(qū)塊每種地物都要嘗試放置,而具體放置多少怎么放置與放置修飾器有關(guān)。下面說的“指定”都可以通過 JSON 配置整數(shù)、浮點(diǎn)、高度提供器或謂詞等,用于生成對應(yīng)的參數(shù)。
放置的嘗試次數(shù)與放置修飾器 RepeatingPlacement 的子類有關(guān)。
CountPlacement:指定數(shù)字區(qū)間的嘗試次數(shù)。
NoiseBasedCountPlacement:使用生物群系噪聲 BIOME_INFO_NOISE 的噪聲值進(jìn)行放置。每當(dāng)噪聲值上升一個(gè)區(qū)間就加 1。
NoiseThresholdCountPlacement:使用生物群系噪聲 BIOME_INFO_NOISE 的噪聲值進(jìn)行放置。如果噪聲值超過某一個(gè)值就使用設(shè)置的第一個(gè)值,否則使用第二個(gè)。
生物群系噪聲 BIOME_INFO_NOISE 是一個(gè)和種子無關(guān)的噪聲,也就是說在不同的世界中,如果有一個(gè)位置的生物群系相同,那么使用后兩個(gè)放置修飾器的同一種地物的生成嘗試次數(shù)就是相同的。
生成地物時(shí)默認(rèn)位置是在區(qū)塊的最低子區(qū)塊的 (0, 0, 0) 處。為了讓區(qū)塊內(nèi)的地物合理放置,還需要其他放置修飾器用于修改地物的放置點(diǎn)。下面這些修飾器就是做這些工作的:
CountOnEveryLayerPlacement:嘗試生成指定個(gè)數(shù)的點(diǎn),隨機(jī)分配到區(qū)塊內(nèi)的水平坐標(biāo)。對于每個(gè)水平坐標(biāo),找到最高的地表位置作為最終的放置位置。
EnvironmentScanPlacement:對于輸入的放置點(diǎn),嘗試在指定步數(shù)內(nèi)按指定方向找到一個(gè)滿足指定條件的位置作為最終放置點(diǎn)。
HeightmapPlacement:將放置點(diǎn)移動到水平坐標(biāo)上的某一高度圖高度處。
HeightRangePlacement:將放置點(diǎn)移動到指定的高度上。
InSquarePlacement:將放置點(diǎn)水平移動到區(qū)塊的某個(gè)位置上。
RandomOffsetPlacement:以放置點(diǎn)為中心,將放置點(diǎn)移動到指定高度和寬度空間內(nèi)的某個(gè)位置。
CarvingMaskPlacement:獲取整個(gè)區(qū)塊內(nèi)所有包含指定雕刻標(biāo)記的位置。
初步?jīng)Q定地物是否能放置在某個(gè)位置的放置修飾器都是 PlacementFilter 的子類。
BiomeFilter:檢查放置點(diǎn)的生物群系,如果群系不對應(yīng)則放棄在此點(diǎn)的放置。驗(yàn)證時(shí)使用從生物群系單元縮放到方塊后的生物群系。
BlockPredicateFilter:如果放置點(diǎn)的方塊不通過方塊謂詞測試,則此放置點(diǎn)無效。
RarityFilter:如果隨機(jī)數(shù)大于
,則此放置點(diǎn)無效。相當(dāng)于平均每
次生成才能成功一次。
SurfaceRelativeThresholdFilter:當(dāng)生成點(diǎn)位于某個(gè)高度圖高度偏移的某個(gè)區(qū)間時(shí)生成點(diǎn)才有效。
SurfaceWaterDepthFilter:當(dāng)放置點(diǎn)水平坐標(biāo)的最高非空氣方塊和最高可阻擋方塊的高度差值小于某個(gè)值時(shí),或簡單來說此位置最高位置上的水深小于某個(gè)值時(shí),此放置點(diǎn)有效。
這些放置修飾器在定義時(shí)是有順序的,通常來說決定嘗試次數(shù)的修飾器和 RarityFilter 在前,之后是修改放置點(diǎn)的修飾器,最后是過濾修飾器。這種順序能保證地物的生成數(shù)量和位置更加正確。
以小型鉆石團(tuán)簇舉例,它的已放置的地物定義如下:
從上方可以看出小型鉆石團(tuán)簇有下面的修飾器:
放置嘗試(7次)-> 水平選擇隨機(jī)位置 -> 隨機(jī)選擇高度 -144~16,三角形分布 -> 檢查生物群系
由于地物本身是世界生成的一部分,所以在生成時(shí)它的隨機(jī)數(shù)發(fā)生器種子固定。對于某一個(gè)地物,它的隨機(jī)數(shù)種子是 ,其中
是這一階段這個(gè)地物放置的序號,
是地物放置階段的序號。由于地物種子的微妙關(guān)系,使不同地物的放置位置產(chǎn)生了微妙的聯(lián)系,也就是“定位法”。

在 1.18 正式版前,鉆石和圓盤的放置點(diǎn)是有聯(lián)系的。它們都處于 UNDERGROUND_ORES 階段生成,所以 相同,不同的只有
。在 1.18 前,地物的隨機(jī)數(shù)發(fā)生器使用的是 Java 的線性同余隨機(jī)數(shù)發(fā)生器,這使得最終的隨機(jī)數(shù)出現(xiàn)了聯(lián)系,也就是上表。在 21w37a 時(shí),這個(gè)規(guī)律仍然存在,但是因?yàn)檫@個(gè)版本中修改了某些地物的順序,造成青金石代替了原先鉆石的位置,所以在 21w37a 中使用 1.17 的方法本質(zhì)上定位的是青金石。真正給定位法帶來嚴(yán)重修改的版本是 1.18pre-7,在這個(gè)版本中隨機(jī)數(shù)發(fā)生器被修改為 Xoroshino128++,最終導(dǎo)致了這個(gè)定位法失效。
由于地物和結(jié)構(gòu)都是按照區(qū)塊生成,并且可以擴(kuò)散到其他區(qū)塊,所以有的時(shí)候區(qū)塊生成順序的不同可能導(dǎo)致地物互相覆蓋,理論上也會導(dǎo)致同一個(gè)種子生成的地物或結(jié)構(gòu)因?yàn)橥婕倚凶呗窂綆淼膮^(qū)塊加載順序的不同而不同。
四. 區(qū)塊光照
在地物生成完畢后,就來到了光照階段。在這個(gè)階段之前,任何方塊的放置都不會進(jìn)行光照的更新,直到這個(gè)階段開始集中更新光照。在光照開始時(shí),這個(gè)區(qū)塊會被加上一個(gè)等級為 33 的 LIGHT 加載標(biāo)簽。在光照計(jì)算結(jié)束后,區(qū)塊的 LIGHT 標(biāo)簽被自動移除。
五. 生成初始生物
在光照計(jì)算結(jié)束后,區(qū)塊進(jìn)入 SPAWN 階段,用于生成世界的初始生物。這個(gè)階段不能修改區(qū)塊內(nèi)的方塊,只能添加實(shí)體。當(dāng)這個(gè)階段開始后,會挑選出所在生物群系動物類別的所有生物進(jìn)行生成。生成時(shí)使用的隨機(jī)數(shù)種子和世界種子、區(qū)塊位置綁定,也就是說在世界生成時(shí)這些生物的位置都是固定的。如果一個(gè)種子的出生點(diǎn)附近初始就生成了一只粉紅羊,那么重新創(chuàng)建一個(gè)一模一樣種子的世界它也依然會生成。
六. 生成完畢
生成初始生物后,就到了 HEIGHTMAPS 階段,但這個(gè)階段并沒有實(shí)質(zhì)作用,因?yàn)楦叨葓D在之前就已經(jīng)生成并填充完畢?;蛘吒珳?zhǔn)的來說,高度圖隨區(qū)塊狀態(tài)的變化而變化。在地物生成前只有兩個(gè)高度圖:OCEAN_FLOOR_WG 和 WORLD_SURFACE_WG,而在地物之后會變成四個(gè)高度圖:OCEAN_FLOOR、WORLD_SURFACE、MOTION_BLOCKING 和 MOTION_BLOCKING_NO_LEAVES。這些高度圖隨對區(qū)塊內(nèi)方塊的修改而變化,所以這個(gè)高度圖階段本質(zhì)上沒有什么作用。
在這個(gè)階段后,就會到達(dá) FULL 階段,代表了區(qū)塊生成完畢。此時(shí)原型區(qū)塊會變?yōu)槭澜鐓^(qū)塊,可以進(jìn)行正常的區(qū)塊行為,比如計(jì)劃刻和隨機(jī)刻等。

到這里,世界生成的過程就結(jié)束了,這系列專欄的內(nèi)容也就到這里了。
源代碼:1.19.4,Mojang Mapping,CFR 0.152 反編譯。
有錯(cuò)誤可以在評論區(qū)指出。