簡而言之: 程序化散佈只有可複現時才值得信任——同樣的輸入必須在每一台機器、每一次建置 裡都產生同樣的森林。這意味著理解種子(如何讓隨機性可重複)、為什麼一個存活的程序化系統在 原始碼控制下是危險的,以及為什麼把散佈烘焙成原生引擎實例,能把一個不可合併的黑盒變成一個 可 diff、可審查、可版本化的資產。本文涵蓋確定性、種子,以及如何防止一支團隊的植被在所有 人腳下悄悄改變。
看起來隨機,但對一支團隊而言這必須是可重複的隨機——在你的機器、隊友的機器和建置伺服器 上都一致。確定性才是讓一份漂亮的散佈變成你真的能出貨並維護之物的關鍵。
為什麼確定性重要
如果散佈在每次打開或建置時都以不同方式重新生成,你的關卡就會悄悄改變——碰撞會 挪動、隱藏物件會冒出來、玩測停止可複現。確定性的散佈,永遠從同樣的輸入產出完全相同的結果。
不可重複的隨機性在關卡裡就是一間產 bug 工廠。如果在另一台機器上打開這場景、或者下週重構它 時,會把每一塊石頭、每一叢灌木落到哪兒重新洗牌——那麼一處曾經清空的位置現在被堵住了,你調 過的視線斷了,玩測的結果也不能信,因為被測的關卡不是你出貨的那一份。確定性——同樣輸入、同 樣輸出、始終如此——才是讓程序化散佈安全的東西。它是「電腦放了一些植物」與「關卡有一個我 可以依賴的、被定義好的、穩定的狀態」之間的差別。
種子:讓隨機性可重複
種子是一段隨機序列的起始數字。同樣的種子 → 每次都得到同樣的「隨機」擺放。把種 子暴露出來並保存,你的散佈就是可複現的;把它交給真正的隨機,就是每次執行都是不同的關卡。
電腦並不做真正的隨機——它跑的是看起來隨機的確定性序列,每一條序列由一個叫種子的起始 數字定義。餵進同樣的種子,你就得到同樣的序列,因此得到同樣的擺放,每一次都是。這正是可複現 散佈的關鍵:工具暴露一個種子,你把它隨場景保存下來,森林就成了(種子 + 參數 + 表面)的 純函數。改一下種子去探索你喜歡的另一種排布,然後鎖住它。任何反過來從一個真正未被播種的 隨機源取值的做法——時鐘、每次打開新一次投擲——都會產生一個每次載入都不同的關卡,這在生產 裡根本沒法用。
版本控制的問題
以「一個系統 + 參數」形式存下的存活程序化散佈,對於原始碼控制而言就是一個黑盒 ——你沒法 diff 它、沒法審查它,兩個人同時編輯就會產生無法合併的衝突。團隊需要散佈以一種 Git 或 Perforce 真的能追蹤的形態存在。
這裡就是散佈撞上團隊現實之處。如果你的森林只作為「一個用了這些設定的散佈系統」存在,原始碼 控制看不到它產生了什麼——只能看到某些參數變了。你沒法在一個 pull request 裡審查一次擺放 上的改動、也說不清什麼在挪動;如果兩個美術碰同一份散佈,你就會得到一個沒有工具能有意義地 解決的合併衝突。一個只活在存活生成器裡的散佈關卡,對團隊為了安全協作而依賴的那些系統,正是 不可見的——這也是為什麼「一切都是程序化的」會悄悄變成「沒人能安全地編輯這片森林」。
這裡的每一個都是一個帶位置、旋轉和縮放的實例。當散佈被烘焙成原生實例時,它們就成了真實 的、可被檢查的資料——森林不再是一個公式,而變成你可以逐行審查的東西。
烘焙讓它變得可 diff
把散佈烘焙成原生引擎實例——帶具體 transform 的真實擺放物件。現在它就是普通的 場景資料:可 diff、可審查、可合併,並且在每台機器上都一致,因為它是被存下的,而不是被 重新生成的。
解決確定性和版本控制兩件事的,是同一招:烘焙。你不是去出貨一個存活的生成器,而是把散 佈烘焙成簡單的原生引擎實例——真正擺放好的 mesh,具體的位置、旋轉、縮放存在場景裡。這一步 一次性把每一個問題都塌陷掉。它按定義就是確定性的(這是被存下的資料,不是又一次投擲)。它 可 diff(原始碼控制看得到變化了的具體 transform)。它可審查(一位 lead 能在 pull request 裡精確地看到什麼挪了)。而且它在執行時對引擎不會帶來任何異常成本,因為不過就是實例化的 mesh。一個先用規則塗密度、再烘焙成原生實例的工作流——Numivo 就正是這麼 工作的——把程序化散佈的作者速度和手放置內容的穩定性一起給你,而沒有黑盒的脆弱性。
保持一支團隊的植被穩定
鎖住種子、提交前先烘焙,並把烘焙後的結果當作事實之源。有意地去重新散佈(調 種子、看 diff),永遠不是意外——這樣森林只有當有人有意去動它時才會變。
一個團隊實用的紀律很短。鎖住種子,讓散佈可複現。提交前先烘焙,這樣進入原始碼控制的 是所有人共享的具體結果,而不是一份在每台機器上重新投擲的配方。把烘焙後的實例視作事實 之源——你審查並出貨的那件東西。而當你確實想改森林時,有意地去做:調參數或種子、重新 烘焙、像對待任何其他修改一樣審查 diff。目標是一片森林,只有當人決定它應該變時它才變,而 且它的每一次變化都是可見且可審查的——就是你會對關卡任何其他部分持有的同一標準。為速度用 程序化、為穩定烘焙下去:這就是全部技巧。
值得偷的現場數字
- 確定性法則:同輸入 → 同輸出,始終如此——否則你的關卡就在漂移
- 一個種子讓隨機性可重複——暴露它、保存它、鎖住它
- 存活的程序化散佈對 Git/Perforce 而言是黑盒——不可 diff、不可合併
- 烘焙成原生實例 → 可 diff、可審查、可合併、處處一致
- 團隊習慣:鎖種子 → 烘焙 → 提交;有意地重新散佈,絕不出於意外
迷你問答
難道我不能就讓散佈保持程序化、永遠不烘焙嗎?獨自的快專案也許可以。對一個團隊或者你打 算維護的東西——不行,你會失去 diff、審查和安全合併,還冒著悄悄漂移的風險。烘焙才是讓程序 化散佈在生產上安全的東西。
那我究竟該把什麼放進原始碼控制——設定還是結果?烘焙後的結果就是事實之源;設定/種子也 一起留著,好讓你能有意地重新生成。只提交設定意味著原始碼控制看不到關卡裡到底變了什麼。
兩個美術編輯了同一片森林——我們怎麼合?用烘焙後的實例,它就是普通場景資料,你正常的 合併工具就能用。用一個存活的生成器,往往就是解決不了的——這就是「提交前先烘焙」和「協調好誰 擁有某片散佈」的核心論點。
烘焙會不會讓我失去以後調整的能力?不會——種子和參數都留著,重新烘焙就是你要變排布時有 意為之的一步。你既能迭代又能穩定,只要重新散佈是一次有意的、被審查的動作,而不是自己 發生的事。
程序化散佈只有當它可複現時才配得上它的位置。鎖住你的種子、烘焙成原生實例,並把烘焙後的 結果當作關卡的真相——你的森林就成了一整支團隊都能在其上建造的東西:作者快、出貨穩、只有 當有人有意去動它時才會安全地變。

