游戏编程中的数学——随机数字生成(RNG)的黑暗秘密
大家好,你們能聽到我講話嗎?這個(gè)演講的內(nèi)容是介紹RNG(隨機(jī)數(shù)字生成)的一些黑暗秘密。如你在大屏幕上看到的,Squirrel已經(jīng)介紹了一些RNG的基礎(chǔ)概念。首先,我想詳細(xì)講解幾點(diǎn)。他的演講更偏重理論,而我的更偏重實(shí)際應(yīng)用一些,不僅僅會(huì)討論一些在游戲開發(fā)中遇到的RNG相關(guān)的潛在的問題,還會(huì)介紹解決這些問題的工具。這個(gè)題目“黑暗秘密”不太好,會(huì)讓人以為Squirrel探討了黑暗的、復(fù)雜的數(shù)學(xué)公式,不過我們的重點(diǎn)是談?wù)撊绾问褂媚切?shù)學(xué)公式。
首先我是誰?為什么你們要聽我的演講呢?這些是我參與制作的游戲。
?
有很多!在一些大工作室,我參與了《爐石傳說》的制作,設(shè)計(jì)了游戲的一些原型,這是我制作的最好的一款游戲了。我只在那里工作了一年,當(dāng)然是制作CCG(交換卡牌游戲),其中有很多隨機(jī)數(shù)字生成。我獨(dú)立開發(fā)過很多游戲,比如右下角的《Overland》,是一款roguelike類型游戲,其中有很多隨機(jī)生成的關(guān)卡。現(xiàn)在我在Direwolf工作,位于科羅拉多州的丹佛市,我仍然負(fù)責(zé)開發(fā)CCG。也許以后會(huì)在這干下去,也許不會(huì)。
不過我們來談?wù)勈褂肦NG吧。我們從一些基礎(chǔ)的、作為一名程序員在這個(gè)領(lǐng)域必須要面對(duì)的問題決策開始。這款解謎游戲《Connectrode》是我之前在2011年制作的,我的妻子非常喜歡,試玩了這款游戲的未成品。
這款俄羅斯方塊風(fēng)格的游戲會(huì)隨機(jī)生成一些方塊。有一天晚上我的妻子向我抱怨道:在一行中出現(xiàn)了9個(gè)顏色相同的方塊!我意識(shí)到我需要修復(fù)這個(gè)bug。我采用俄羅斯方塊游戲所使用的方法,有時(shí)人們將這個(gè)方法稱為套袋方法(Bagging)。
?
這是用來為你的游戲加入隨機(jī)性的一個(gè)基礎(chǔ)方法。如果你像我一樣每一輪都投擲一次骰子來從6個(gè)數(shù)字中隨機(jī)得到一個(gè)數(shù)字,你會(huì)得到一個(gè)均勻分布。不過有的時(shí)候你不希望這個(gè)隨機(jī)性過于隨機(jī),一個(gè)簡單的方法是——這其實(shí)是一個(gè)設(shè)計(jì)決定,或者桌游設(shè)計(jì)者會(huì)這樣說——“這里不應(yīng)該使用骰子,而應(yīng)該使用一副紙牌。”從數(shù)學(xué)上來講,投骰子是對(duì)一個(gè)隨機(jī)集合進(jìn)行取樣,允許樣本的重復(fù),取樣不會(huì)改變?cè)摷?#xff1b;而使用紙牌的話,你從中抽出一張牌后這張牌不會(huì)回到牌組中。
?
這樣可以確保在分布中不會(huì)產(chǎn)生重復(fù)的結(jié)果。多數(shù)使用套袋方法的游戲都有一個(gè)套袋,套袋中包含一個(gè)隨機(jī)集合。考慮紙牌的洗牌。我們可以使用三副紙牌洗牌——實(shí)際上拉斯維加斯的賭場現(xiàn)在就是這樣做的,為了防止人們?cè)诤诮芸?1點(diǎn)游戲中作弊。他們把三副或者更多副紙牌混在一起洗牌,這樣會(huì)改變紙牌分布的隨機(jī)性。因此如果你發(fā)現(xiàn)相同的分布規(guī)律一次又一次地出現(xiàn),那么你要重點(diǎn)考慮一下它,這種情況對(duì)我來講并不常見,不過你應(yīng)該使分布更加平穩(wěn)。另外我們還要考慮邊緣情況。如果你同時(shí)洗三副牌,然后當(dāng)沒有牌的時(shí)候重新洗牌,首先你會(huì)想到你能得到的最大組合是三個(gè)黑桃尖。接著如果你重新對(duì)一副新牌洗牌,這時(shí)會(huì)產(chǎn)生一種邊緣情況——字面意義上的邊緣情況,即在一個(gè)套袋的末尾你有可能得到三個(gè)黑桃尖,而在另一個(gè)套袋的開始你又得到了三個(gè)黑桃尖。
?
這個(gè)邊緣情況作為隨機(jī)性的一個(gè)特殊情況值得考慮,因?yàn)檫@是RNG的第一個(gè)黑暗秘密:如果你沒有提前計(jì)劃使其不可能發(fā)生,那么每個(gè)隨機(jī)的邊緣情況可以發(fā)生,(最終!)將會(huì)發(fā)生在一個(gè)玩家上。
?
作為一名游戲程序員,你是確保這個(gè)問題不發(fā)生的最后一道防線,有的時(shí)候你要和設(shè)計(jì)師一起解決這個(gè)問題。我在Direwolf的這些CCG設(shè)計(jì)師同事,他們非常擅長數(shù)學(xué)。不過我也有和一些擅長美術(shù)的設(shè)計(jì)師工作過,他們的數(shù)學(xué)應(yīng)用能力這方面很糟糕,所以有的時(shí)候程序員要站出來說“你考慮過這些了嗎?”即使這通常是設(shè)計(jì)師的工作。
這里我引用了話劇《Rosencrantz and Guildenstern Are Dead》,其中的角色在整部劇中不斷地投擲硬幣,每次都是正面。這個(gè)概率非常小,不過在游戲中有的玩家有可能會(huì)擲出100次壞點(diǎn)。如果在數(shù)學(xué)上可能,那么最終一定會(huì)發(fā)生。你要把這點(diǎn)考慮進(jìn)去。而這個(gè)問題的解決方法就是……這是暴雪的開發(fā)者發(fā)表的一篇藍(lán)帖,介紹了《暗黑破壞神3》使用的方法,對(duì)于這種打裝備的游戲來說,這個(gè)問題的解決非常重要。他們加入了一個(gè)我稱之為“保底機(jī)制(Pity Timer)”的東西。
?
這里的藍(lán)字解釋道:“掉裝備的概率很低。而對(duì)于有的玩家總也打不到一個(gè)裝備。”因此他們加入一個(gè)保底機(jī)制,以確保一段時(shí)間不掉裝備后一定會(huì)掉裝備,使其按照預(yù)期的機(jī)制運(yùn)行,即玩家最后總會(huì)打出傳奇裝備。RNG最大的黑暗秘密是——你會(huì)時(shí)常遇到這個(gè)問題,我想詳細(xì)談?wù)勥@一條——就是總的來講,人的大腦不是很擅長處理概率問題。
?
在很多情況下,人腦很容易錯(cuò)誤地理解概率,因此引入諸如保底機(jī)制之類的機(jī)制可以……嗯……玩家知道裝備有百分之一的掉落概率,然后殺了二百只怪,他明白從邏輯上有可能殺的這兩百只怪什么裝備都沒有掉落,不過他還是會(huì)很不爽地抱怨。他們沒有預(yù)料到會(huì)經(jīng)常出現(xiàn)這種情況,除非坐下好好地進(jìn)行一番數(shù)學(xué)計(jì)算。而我們有關(guān)RNG的很多技巧模擬了他們的預(yù)期,使得隨機(jī)性朝著玩家認(rèn)為應(yīng)該的那樣進(jìn)行,即使在數(shù)學(xué)上并不正確。保底機(jī)制正是一個(gè)例子。不過玩家仍然會(huì)經(jīng)常抱怨RNG不公平,討厭它,詛咒它……
事實(shí)上我之前恰巧聽到這個(gè)故事,有個(gè)叫做《Urban Dead》的游戲,一個(gè)MMO網(wǎng)頁游戲,我很喜歡玩。游戲中包含隨機(jī)點(diǎn)數(shù),如果你點(diǎn)擊攻擊按鈕,系統(tǒng)會(huì)擲點(diǎn)決定攻擊點(diǎn)數(shù)。一個(gè)玩家寫道:如果在溝槽中點(diǎn)擊攻擊按鈕,然后進(jìn)行攻擊,然后在溝槽中等待八秒鐘后再次攻擊,他總會(huì)攻擊成功。而在游戲的維基頁面的頂端,這名開發(fā)者說道:“這根本不可能!我使用一個(gè)隨機(jī)數(shù)字生成器了!”
?
如果你之前聽了Squirrel的演講,那么你應(yīng)該不會(huì)被這點(diǎn)驚到。不過結(jié)果證明玩家是正確的。開發(fā)者只是簡單地將當(dāng)前時(shí)間以秒為單位輸入到rand()函數(shù)中。而Squirrel的演講提到了它最低的兩位其實(shí)是不可靠的。因此每過八秒鐘奇怪的事情可能就會(huì)發(fā)生。我覺得這個(gè)開發(fā)者很滑稽,首先否定了這個(gè)問題,后來才意識(shí)到這個(gè)問題。所以你不能輕易地種種子。在我們的例子中我們可能犯這個(gè)錯(cuò)誤,所以你要透徹地思考這個(gè)問題以便你的游戲中不會(huì)出現(xiàn)此類bug。玩家們通常會(huì)懷疑此類按鈕有bug,懷疑RNG是否出了問題。不過總會(huì)有巧合發(fā)生嘛。好了,這是游戲編程的黑暗秘密,我要把這些概念綜合在一起:對(duì)于任何可能的事情,設(shè)計(jì)師總會(huì)改變他們的思路。
?
這對(duì)于打裝備的游戲尤其適用。我的觀點(diǎn)是,如果在設(shè)計(jì)一款打裝備的游戲,或者以打裝備為主的游戲時(shí),設(shè)計(jì)師應(yīng)該對(duì)其進(jìn)行調(diào)整,因?yàn)檫@會(huì)對(duì)玩家體驗(yàn)以及一些你可能想不到的事情造成巨大影響,我們一會(huì)兒會(huì)提到。沒錯(cuò),玩家獲得一件傳奇裝備的體驗(yàn)非常重要,他們要確保有保底機(jī)制,使玩家殺死boss足夠多的次數(shù)后一定能得到傳奇裝備。
在這里我想要介紹一個(gè)工具,它能讓你控制裝備的掉落,詳細(xì)來說這是一個(gè)查找表。沒錯(cuò),查找表可以表示骰子,也可以表示一副紙牌,或者其它的東西。一個(gè)加權(quán)的查找表可以這樣創(chuàng)建:假設(shè)有52個(gè)元素,每個(gè)元素代表一張牌,每張牌的起始權(quán)重為1。如果權(quán)重可以被修改,那么這就成了一個(gè)強(qiáng)力的工具。你可以將一張牌的權(quán)重減為零,當(dāng)它被抽走后,然后在后面再重新設(shè)置。你還可以表示很多其他內(nèi)容。這里我用來表示打boss掉的裝備,普通裝備權(quán)重為1,罕見裝備權(quán)重為0.5,罕見裝備掉落的概率為普通裝備的一半。
?
這里我實(shí)現(xiàn)了一個(gè)保底機(jī)制。首先這是嵌套在查找表中的查找表,有助于你響應(yīng)設(shè)計(jì)者的要求,使你有很大的自由可以修改裝備掉落的參數(shù),并且加入一些細(xì)節(jié),就像我們?cè)谶@里為傳奇裝備加入保底機(jī)制那樣。在下一張幻燈片我會(huì)介紹一些更復(fù)雜的情況。
動(dòng)態(tài)加權(quán)是另一個(gè)實(shí)用的工具,在這個(gè)例子中根據(jù)保底機(jī)制傳奇裝備的加權(quán)會(huì)動(dòng)態(tài)改變,最終會(huì)掉落。大家能看到我的鼠標(biāo)嗎?實(shí)際上這里我加入了回調(diào)函數(shù),這第二個(gè)的意思是每當(dāng)?shù)袈涞难b備不是傳奇別時(shí),傳奇裝備的權(quán)重會(huì)增加。不過這仍然有悖于我的規(guī)則,即你應(yīng)該考慮所有的邊緣情況,從數(shù)學(xué)上講你沒有百分之百地確保傳奇裝備最終一定會(huì)掉落,除非同時(shí)減少其他元素的權(quán)重。這是另一種實(shí)現(xiàn)這個(gè)機(jī)制的方法。不過在這個(gè)例子中我只是在傳奇裝備沒有掉出的時(shí)候簡單地增加它的權(quán)重而已。當(dāng)?shù)袈浜?#xff0c;將它的權(quán)重設(shè)置為初始值。這是一個(gè)很好的可維護(hù)的方法。
將這些串在一起:剛才我展示的一款我獨(dú)立開發(fā)的游戲《Angry Henry And The Escape From The Helicopter Lords: Part 17, TheRe-Reckoning》,沒錯(cuò)這個(gè)就是全名,為了節(jié)省我們的時(shí)間我爭取不再重復(fù)它。在這個(gè)MMO游戲中,大boss是WalrusCopter,殺死他會(huì)掉裝備,分為普通,罕見和傳奇三個(gè)不同級(jí)別。
?
這是最初的實(shí)現(xiàn)方法,而現(xiàn)在我為傳奇別裝備加入了之前我們探討的保底機(jī)制,使它的權(quán)重動(dòng)態(tài)改變。
?
這里還加入了另外一層考慮,我聽說很多打裝備的MMO都有這個(gè)問題,即裝備和游戲的經(jīng)濟(jì)緊密相關(guān)。我們的這個(gè)MMO游戲最初使用這個(gè)簡單的實(shí)現(xiàn),后來設(shè)計(jì)人員表示“我們遇到了一個(gè)問題,騎士使用的傳奇裝備在拍賣所的價(jià)格要比武士的傳奇裝備貴,我們應(yīng)該改變這一點(diǎn)。”該如何回應(yīng)這個(gè)問題呢?我推薦使用查找表的嵌套功能,為傳奇裝備創(chuàng)建子查詢表,這樣我們不僅有保底機(jī)制,還可以根據(jù)各職業(yè)的玩家人數(shù)設(shè)置不同職業(yè)裝備的權(quán)重,這涉及到供求關(guān)系,你控制了裝備的供給那么需求自然會(huì)上升。
以上是我實(shí)際遇到的一個(gè)問題。你可以通過使用如查找表之類的工具使你的代碼盡量整潔且易于維護(hù),以應(yīng)對(duì)一些意想不到的問題,我推薦你使用這些工具。
我們來看這個(gè)例子:游戲系統(tǒng)生成這個(gè)裝備,生成這個(gè)裝備,你可以看到傳奇裝備的權(quán)重在增加,直到最后生成傳奇裝備。
?
如果一直沒有生成傳奇裝備那么它生成的概率會(huì)不斷增加。然后會(huì)進(jìn)入下一步的查找表,投出另外一個(gè)隨機(jī)點(diǎn)數(shù)。每顆鉆石代表一個(gè)隨機(jī)的點(diǎn)數(shù)。命中后傳奇裝備的權(quán)重會(huì)重置。查找表是一個(gè)很有用的工具,可以用來表示任何類型的隨機(jī)性,包括更簡單的情況,可以用在嵌套的樹形圖中,使代碼整潔并易于維護(hù)。
接下來,我想拓展一下Squirrel沒有深入探討的問題,即使用他的Squirrel噪音庫實(shí)現(xiàn)一些不同的隨機(jī)散列,包括隨機(jī)生成一個(gè)世界。我想展示一下如何使用散列解決和RNG相關(guān)的問題,其中包含“深度重復(fù)(Deep Echoes)”問題。
?
我會(huì)解釋它的含義。這是游戲中另一個(gè)比較奇怪的地方,有的玩家留言稱他們?cè)诓煌澜缰g隨機(jī)穿梭時(shí),在一個(gè)世界中看到了一座和另外一個(gè)世界中相同的城市。
?
這種情況可能發(fā)生,因?yàn)橥ǔT谶@種游戲中你要種種子,買QQ號(hào)平臺(tái)將種子賦給城市,然后城市會(huì)根據(jù)被賦予的種子隨機(jī)生成它的構(gòu)成。在這個(gè)例子中,我們有一個(gè)名為“僵尸宇宙(ZombieUniverse)”的宇宙,我們?yōu)檫@個(gè)頂層的宇宙設(shè)置一個(gè)種子,然后它使用該種子生成一系列的子級(jí),即下一層的星系,然后星系又生成不同的太陽系,每個(gè)太陽系又生成不同的行星,每個(gè)星球上又有不同的城市,以及沒有顯示在這里的,每個(gè)城市隨機(jī)生成的居民。
我在這里標(biāo)記為紅色的地方是你可能遇到的一種奇怪的情況。首先解釋一下,上一層種子的作用是為了生成下一層的種子,因此頂層宇宙的種子被用來生成每個(gè)星系的種子,每個(gè)星系的種子是使用上一層的種子通過RNG得到的。那么最終我們會(huì)遇到的一個(gè)問題是有可能隨機(jī)種子在不同的情況中被使用了兩次,如果下面所有的內(nèi)容都是使用那個(gè)種子生成的,那么你會(huì)得到完全相同的樣式。在這個(gè)例子中,這兩個(gè)太陽系中的所有內(nèi)容都完全相同——它們所包含的行星,以及行星中的城市,甚至城市中的居民以及他們的名字、職業(yè)都是完全相同的,任何隨機(jī)生成的內(nèi)容現(xiàn)在卻彼此重復(fù),而游戲原本應(yīng)該是隨機(jī)的,充滿噪音的,不可預(yù)測的,然而現(xiàn)在情況卻恰恰相反。我甚至昨天晚上還在解決這個(gè)問題,最終得到了一個(gè)解決方案。這個(gè)方法有一些要注意的地方,我想要分享一下我們探索解決這個(gè)問題的過程,尤其是使用隨機(jī)散列作為工具,雖然對(duì)于我們的結(jié)局方法不是最好的工具。那么,我來展示一下Unity中的一個(gè)項(xiàng)目來說明這個(gè)問題。希望你們都能看清楚。好了,我們稍等一下它生成,這個(gè)時(shí)間它還檢查了是否有重復(fù)。在這個(gè)嵌套的結(jié)構(gòu),太陽系包含行星,行星包含城市,城市包含居民。由于這是個(gè)“僵尸宇宙”,所以居民的名字都是RARGB,ABGGA,RRGGA,看大家都在笑我還是別讀了……
?
BRRRR!這個(gè)城市一定很冷,在北半球上吧……我們花了很長時(shí)間來解決這個(gè)問題,你會(huì)發(fā)現(xiàn)仍然有一些城市的種子是一樣的。同樣的名字或者種子。這里檢查種子是否相同。現(xiàn)在我先不使用它。我要說明的是最終我們選擇使用不同的算法來生成下面層級(jí),而不是只依賴上一層的種子或者其它數(shù)據(jù),這樣可以避免下一層級(jí)的重復(fù)。假設(shè)我要生成一個(gè)行星,我需要生成那個(gè)行星的內(nèi)容,在這個(gè)例子中即行星的名字。為了生成它的子級(jí),我想要使用一個(gè)其它的種子,該種子是該行星獨(dú)有的。在這個(gè)例子中,你可以看到種子的數(shù)值,除此之外還有個(gè)索引值(Index)。很明顯,這三個(gè)子級(jí)的索引分別為0,1,2。然后我可以使用它來單獨(dú)識(shí)別出這個(gè)行星。等一下……這里有兩個(gè)行星的名字相同……哦,它們的種子不一樣!好了,重點(diǎn)是你可以使用某一層級(jí)的索引沿著鏈條向上遞歸以識(shí)別該特定層。
直到24小時(shí)以前我依舊使用的是我的第一個(gè)解決方法——我對(duì)這個(gè)方法并不滿意——即我們預(yù)先生成所有的種子,然后使用暴力算法確保唯一性。我并不想采用這個(gè)方法,原因之一是我們另外限制了每層可以擁有的子級(jí)的數(shù)量(即限制了最大索引數(shù)),然后只要我們生成的內(nèi)容數(shù)量沒有溢出,我們可以取唯一的索引數(shù),將它們加在一起,然后乘以底數(shù)3這個(gè)最大的索引數(shù),而不是底數(shù)2或者底數(shù)10,這樣構(gòu)建出一個(gè)獨(dú)特的整數(shù)代表該元素的地址,這個(gè)元素遞歸的索引。有了這個(gè)后,我們可以將它作為一個(gè)隨機(jī)數(shù)字的查找表。這個(gè)方法有些笨拙,因?yàn)槲覀兪┘恿艘恍┫拗啤?br /> ?
我們發(fā)現(xiàn)的一個(gè)更佳的方法是使用隨機(jī)散列;將這個(gè)N維地址輸入到一個(gè)散列函數(shù),就是Squirrel剛才展示的那個(gè)函數(shù)。嗯……這一行可能不是那么好懂,我來解釋一下:在C#中這一行的作用是把所有的層級(jí)都放入到一個(gè)數(shù)組中,然后將其輸入到散列函數(shù)中。我們可以將任意數(shù)量的整數(shù)輸入到該函數(shù),并會(huì)輸出一個(gè)唯一的大隨機(jī)數(shù)。
?
不過這個(gè)例子不保證唯一性,因此仍有重復(fù)的可能性。現(xiàn)在行星會(huì)基于其父級(jí)的種子生成它的內(nèi)容,但是當(dāng)生成子級(jí)時(shí)它使用的是對(duì)它來講唯一的東西,這樣就可以避免生成同樣的子級(jí)了。這是InitializeChildren方法,該函數(shù)會(huì)生成種子。
?
事實(shí)上,每當(dāng)我要使用種子生成一個(gè)特定的元素,比如行星,居民(事實(shí)上居民不會(huì)調(diào)用該函數(shù),因?yàn)樗鼪]有任何子級(jí))等,我使用頂層(即宇宙)的種子。在實(shí)際使用中我發(fā)現(xiàn)我需要改變每一層預(yù)先生成的種子,所以如果行星擁有同樣的索引那么它們的名字會(huì)相同。這個(gè)例子展示了現(xiàn)在的情況:現(xiàn)在我們可以有兩個(gè)太陽系,它們的隨機(jī)種子值相同,不過它們的子級(jí)不一樣了,不會(huì)再發(fā)生重復(fù)情況了。
?
我要解釋一下為什么這很重要。當(dāng)然你不希望宇宙的不同部分彼此相同,不過這種重復(fù)情況的確會(huì)發(fā)生,除非你有一個(gè)完美的散列函數(shù),當(dāng)然這樣的散列函數(shù)并不存在。最終在某處你一定會(huì)得到兩個(gè)擁有相同種子的元素,而你不希望它們相同。這完全是為了玩家的感覺。如果他們不認(rèn)為這些是隨機(jī)的,那么這些就不是,比如我可以生成兩座城市,它們的道路布局可能相同,但是它們的內(nèi)容是完全不同的,那么玩家可能不會(huì)注意到這個(gè)使城市與眾不同的表面上的元素和其它的城市是相同的,因?yàn)樗鼈兊乃猩霞?jí)內(nèi)容都是不同的,而它們的子級(jí)內(nèi)容也不同,因?yàn)槲覀兘鉀Q這個(gè)深度重復(fù)的問題了。這一點(diǎn)很重要。
?
對(duì)了……忘記解釋這個(gè)例子一個(gè)重點(diǎn)了,也許你已經(jīng)注意到了所有種子的數(shù)值都很小,這里為了示范的效果我將種子的大小限制在0到63之間迫使這個(gè)問題出現(xiàn)。我這樣做是為了突出這個(gè)問題,使大家明白了……首先,大家應(yīng)該看到了使用這個(gè)隨機(jī)散列方法時(shí)一些需要注意的地方,當(dāng)我們不使用唯一的查找表后我們遇到了一個(gè)新的問題,即當(dāng)我生成下一級(jí)的元素時(shí),我使用的是一個(gè)由散列函數(shù)得到的種子,不過這里再強(qiáng)調(diào)一下,這個(gè)散列函數(shù)有可能產(chǎn)生重復(fù)的結(jié)果。現(xiàn)在,的確有可能兩個(gè)太陽系包含同樣的行星,在我的例子中它們的名稱相同,在其他的例子中有可能位置相同。這看上去很糟糕,兩個(gè)行星的城市完全相同,城市的名稱完全相同。
對(duì)于這個(gè)“廣義重復(fù)(Broad Echoes)”問題,我把它留給觀眾們作為一項(xiàng)作業(yè),因?yàn)槲业难葜v超出時(shí)間了。
?
這個(gè)問題其實(shí)沒有一個(gè)完美的答案,因?yàn)閺臄?shù)學(xué)上講如果你有一個(gè)函數(shù),輸入?yún)?shù)的數(shù)量大于輸出參數(shù)的數(shù)量,你總會(huì)遇到鴿巢問題,即如果鴿子的數(shù)量大于巢穴數(shù)量,那么最終總會(huì)有一個(gè)巢穴中有兩只或以上的鴿子。當(dāng)遇到這個(gè)問題時(shí),要在編譯時(shí)預(yù)先生成一個(gè)包含互不重復(fù)的種子列表,不過如果你要求輸出的種子數(shù)超出限制,你仍會(huì)得到重復(fù)的種子。不過這里我們介紹了如何使用一個(gè)元素本身的獨(dú)特性使得下一層生成的內(nèi)容不會(huì)雷同。
看上去要到時(shí)間了,不過我還想推薦兩篇我搜到的文章,它們介紹了這些工具,以及推薦了這些工具的其它使用方法。
?
DanCook,在他的Lost Garden博客上很好地介紹了裝備表,以及它們?nèi)绾吻短?#xff0c;比我介紹的要有深度更詳細(xì)。在Unity的博客上也有一篇博客關(guān)于使用可重復(fù)的隨機(jī)數(shù)字,噪音以及散列,這篇文章令我大開眼界讓我感受到了這個(gè)工具有多么的強(qiáng)大。你可以在上面找到更多信息。我會(huì)將這個(gè)演講內(nèi)容放在MathForGameProgrammers.com上。謝謝大家!
總結(jié)
以上是生活随笔為你收集整理的游戏编程中的数学——随机数字生成(RNG)的黑暗秘密的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从0开始搭建一个战棋游戏的AI(初级教程
- 下一篇: 开发笔记:游戏逻辑模块组织及数据同步