06.27 玄學VS數學:電子遊戲的“偽隨機”設計原理

玄學VS數學:電子遊戲的“偽隨機”設計原理

你希望自己的遊戲含有多少隨機要素?這些隨機要素是什麼性質的?它在哪些層面可能出錯?我建議所有開發者在製作中仔細考慮這個問題。

有些人認為遊戲應該儘量不出現隨機要素,或者只起極小的作用,把所有決定性因素都交給玩家操作。如果一個攻擊會生效,那他產生的反饋同樣應該是確定性的,而非隨機的反映。比如在射擊遊戲中,如果你用槍指著一個人物並扣下扳機,那麼目標應該被擊倒,子彈不應該隨機漂移或受瞄準系統干擾。

還有一些開發者擔心玩家對隨機系統有什麼誤解。比如攻擊命中率是90%,但他們覺得這其實是虛假的數字。在實際的攻擊和“擲骰子”過程中,他們期望有98%的攻擊可以命中。

玄學VS數學:電子遊戲的“偽隨機”設計原理

抽卡手遊所謂的“隨機”多數時候是招人恨的

很少有計算機程序能實現真正的“隨機性”。在我們廣闊的“牛頓世界”中,“完全隨機”的現象其實是相當罕見的。我們以為擲骰子時哪面朝上是隨機的,實際上結果在固體離開我們手掌的時候就已註定了。用“隨機”解釋這些現象,其實是因為我們無知——我們不知道骰子在空中運動的矢量模型,也不知道它撞擊地面的反應,會怎樣翻滾和停止——這對有人壟斷結果收益其實是有利的。

你可以在量子物理層面實現真正的“隨機”,但在日常生活中大多數時候無此技術條件和必要,我們都使用其他替代手段來實現相似的效果。在電子遊戲等娛樂軟件中,一般使用的手段是“偽隨機數生成器”。遊戲中的隨機性,在各個社區已經引起了開發商和玩家注意。本文我以三款經典遊戲為案例,看看它們的偽隨機機制是的本質是什麼、如何運作,以及在某些情況下,是如何被利用的。

《最終幻想》:從預先生成的數字列表中依次讀取

初代《最終幻想》的主程序是傳奇8位機程序員Nasir Gebelli。他在Apple II電腦平臺為自己贏得崇高的聲望。Famicom(下簡稱“FC”)主機與Apple II使用的都是6502芯片,所以Gebelli轉移到FC上可謂如魚得水,將他的精湛技藝和寶貴經驗發揮得淋漓盡致。

《最終幻想》第一個偽隨機運用場景是遇敵。遊戲的ROM中存儲著一份含有256個數字的列表。每次遊戲系統重啟時,該列表的數值讀取進度都會被初始化。玩家每走一步,系統就讀取一個數值,將其與後臺設置的遇敵閾值做比較,如果小於閾值,觸發遇敵;大於閾值則無事發生。換言之,這是一個數列重複排列組合使用的過程。

當然,這不是初代《最終幻想》使用的唯一一套隨機系統。遊戲還有另一套遇敵機制,使用與上面原理相同的數字列表系統——不同的是,該列表與上面使用的列表不是同一份,而是保存在卡帶靠電池驅動的RAM中,與玩家的存檔放在一起,也不會隨著重啟刷新。這就意味著,如果玩家在特定區域存盤,然後在某個位置遇到了敵人,你讀檔後永遠能在相同的位置遇到同一組敵人。有趣的是,這份列表雖然和存檔一同放在RAM上,但通過導出我們發現,兩份文件是互相獨立的,存檔文件本身不包含任何亂數列表。

玄學VS數學:電子遊戲的“偽隨機”設計原理

《最終幻想》

那麼,老遇見相同的怪,豈不是很無聊?

別急,在戰鬥中還有隨機性發揮的空間。引擎程序員做了一個相當聰明的設計,防止亂數序列被玩家破解、利用,從而創造優勢。

在玩家進入戰鬥後,遊戲每刷新2幀就會讀取一次亂數,而戰鬥中卻不會進行遇敵判定。如此高頻率的亂數消耗,使戰鬥結束後,玩家無法在底層運算不可見的情況下判斷亂數究竟讀到了哪裡。所以,雖然讀檔後第一次遇敵結果都一樣,但除非你能精確到幀地複製每次遊玩的操作,後續遊戲體驗很快就會朝未知的方向發散。

對遊戲體驗來說,這種“發散”很重要。我們可以說,《最終幻想》的亂數運行機制無關亂數本身的價值,只是不斷使數字滾動——換言之,將系統計算結果與玩家難以精準還原的操作關聯起來了。

電腦程序在無其他因素干擾的情況下,只會按照一種既定方式運行,導出每次都相同的結果。因此具體到RPG設計中,我們必需小心玩家利用偽隨機機制創造有利狀態,比如避免遇敵,或者為戰鬥挑選最合適的敵人。解決這個問題的辦法就是在系統之外尋找更多變量,改變程序的運行狀態。在FC和SFC上,很多遊戲採用在部分幀或所有幀上不斷讀取亂數的做法。因為玩家的操作很難在相同1幀內重現,隨著時間的推移,程序導出的結果自然也會越來越發散,因為玩家給程序提供了變量。

如果是有時鐘的操作系統,根據時間讀取或重置亂數也是可行的辦法——但時鐘的時間通常是玩家自己設置的,這可能也會導致亂數被利用。然而如果時鐘的單位刻度足夠精準,比如達到毫秒級,那麼根據時鐘設置亂數是非常好用的,因為在毫秒單位內玩家很難做出準確的操作。

回到《最終幻想》。嚴格來說,這是一種過於隨意地“利用”玩家的做法——即便玩家本身也可以做一些操作來改變列表狀態。但是如果某場高難度戰鬥,我們希望玩家儘量避免或者強制觸發,要怎麼辦呢?

在遊戲中有一個隱藏BOSS“死亡機甲”(WarMECH),出現在一個遇敵率非常高的場景裡。每個場景能遇到的怪物都是有分組的,設計師將“死亡機甲”單獨分進“第8組”,並且設定遇敵亂數只有撞上固定的三個數才會碰到“第8組”,因此理論上玩家遭遇“死亡機甲”的概率只有3/256。

玄學VS數學:電子遊戲的“偽隨機”設計原理

與“死亡機甲”對戰

《超級馬力歐64》:線性同餘法

現在基於線性同餘法編寫隨機數生成器是軟件開發採用的主流解決方案,遊戲圈也一樣——當然前提同樣是不被破解和利用。《超級馬力歐64》是使用線性同餘法生成隨機數的代表遊戲之一,任天堂使用這種方法在地圖中大量隨機生成金幣。

線性同餘法就是使用線性同餘方程(編者按:我不是數學帝,這個方程我也不懂,有興趣的可以百度或請教程序員)無限計算,得出一個數列。根據該方程的特性,這個數列在不懂公式的人眼中看起來近似完全隨機,但在N次計算後必定會開始重複之前的結果,形成循環。

玄學VS數學:電子遊戲的“偽隨機”設計原理

《超級馬力歐64》

在遊戲中,玩家行為(比如走幾步路)會生成一個“種子”數值帶入方程算出一個解,這個解本身對應遊戲中的某種結果反饋給玩家,同時本身會作為第二個“種子”數值再次帶入方程運算,如此無限重複,形成一個龐大的數列。

《超級馬力歐64》已經被破解,遊戲使用的底層公式也被曝光了。根據這個公式,任意數值在帶入方程運算65,114次後就會形成循環,開始重複之前的結果。如果是在科研領域,規律如此明顯的偽隨機數肯定是不能做樣本使用的。然而對遊戲來說,這個方程的運算結果“外行”看起來已經相當隨機,而且在進入循環之前給出的數值樣本足夠大,基於數學公式進行運算對程序員來說既方便學習又不吃太多性能,是一種不錯的解決方案。

《精靈寶可夢》:複雜的多重算法結合

《最終幻想》的“古典”偽隨機數生成法可以決定玩家遇到哪些敵人、何時遇敵,但是耗費的工作量是巨大的;《超級馬力歐64》的線性同餘法可以用於大量在遊戲中刷道具或者定義敵人行為,然而從遊戲技術上說,也談不上多大進步。在遊戲業,玩“偽隨機”的集大成者當屬任天堂的另一招牌IP《精靈寶可夢》。

影響玩家遇到一隻寶可夢的亂數可能產生在很早以前,並且在不知不覺中,已經影響你的遊戲進程很長時間了。這些數據有些隱藏在玩家身上,有些是保密的,操縱他們很可能從根本上改變遊戲體驗——而且因為寶可夢是可交換的,受影響的可能不止是你一人。

玄學VS數學:電子遊戲的“偽隨機”設計原理

當年刷神獸個體值可是個苦差事

因為隨機性是遊戲核心玩法的一部分,所以寶可夢繫列使用的偽隨機算法特別複雜,比如“馬特賽特旋轉演算法”(Mersenne Twister)等等。由於該系列在全球範圍內旺盛的人氣,有無數玩家樂此不疲地嘗試破解它的算法,還由此誕生了專門的極客站Smogon University。

破解算法的一個目的是獲取“閃光”寶可夢——它沒有對戰優勢,但極其罕見。在“寶石”系列(第三世代)中,平時遊戲每1幀就會讀取一次隨機數,而在戰鬥中讀取頻率還要翻倍,至於其他隨機元素的複雜程度就更不必說了。Smogon University有一個單獨頁面是專門計算“綠寶石”的隨機數的,因為“綠寶石”在設計上有個缺陷:遊戲重啟後,系統不會根據時鐘設置偽隨機數的種子數值,而是總將其重置為0,在“紅寶石/藍寶石”中則不存在這一問題。

知道哪些時段、哪個區域、哪幾幀裡能遇到“閃光”並不能保證你一定可以得到它。時間精確與否是個問題,而且你在遊戲中的各種行為都可能觸發另一次亂數判定,“吃掉”你期望撞上的那個偽隨機數,但至少玩家還是因此有了個看得見摸得著的目標,能朝這個方向努力。而且恰恰由於綠寶石每次開機會重置種子數值的特性,“刷閃光”還是比普通玩家忍受的1/8192的概率高多了,操作上也比藍寶石/紅寶石更便捷。

玄學VS數學:電子遊戲的“偽隨機”設計原理

決定《精靈寶可夢》亂數的因素非常多

需要注意的是,《精靈寶可夢》中用於生成偽隨機數的重要數值“訓練師ID”及“隱藏ID”是與存檔綁定的,在遊戲流程中不可更改。

破解隨機數的另一個目的是為了刷個體值,抓“高V”寶可夢——這相關的算法在“綠寶石”時代幾乎已經被完全破解了,計算結果非常可靠。然而進入“第六世代”以後,官方大幅降低了“高V”寶可夢的獲取難度,如今已經沒有費半天勁算偽隨機數的必要了。


分享到:


相關文章: