程序化隨機多邊形地圖生成

前言

最近跟團(tuán)隊想要開發(fā)一個開放世界的游戲,這是很有趣的游戲概念,然而參考了《塞爾達(dá)傳說 荒野之息》的設(shè)定后發(fā)現(xiàn),這個游戲的成功很大程度是美工和設(shè)計大量的工作,才形成了這個很有趣的大陸,然而我們的團(tuán)隊沒有辦法手工搭建出這么大的場景,于是我萌生了一個很自然的想法:寫一個自動生成還算合理的地形的腳本。

起初我嘗試了Perlin噪聲直接生成高度圖的做法,然而產(chǎn)生的地形太過極端,腳本的開發(fā)也進(jìn)入了僵局,直到我看到了一篇2010年的文章:
原文地址:
http://www-cs-students.stanford.edu/~amitp/game-programming/polygon-map-generation

這篇文章是在flash上實現(xiàn)了一個自動隨機多邊形地圖的demo,我跟著這篇文章的思路,基本上復(fù)制了一個unity3D的版本,以后可能會再寫出一個unreal的版本。

源碼github地址

效果圖

demo

基礎(chǔ)多邊形細(xì)胞圖

既然要產(chǎn)生一個多邊形為基礎(chǔ)的地圖,那么第一步當(dāng)然是產(chǎn)生一個由多邊形填滿的二維圖,術(shù)語上這種圖形叫做維諾圖(Voronoi Diagram),我更喜歡叫他細(xì)胞圖。

Voronoi

這種圖形的生成原理網(wǎng)上有很多,我參考了這篇文章:

http://blog.csdn.net/k346k346/article/details/52244123

需要注意的是,采用這種方法生成出的圖形太過隨機,我們需要讓他看起來齊整一些,這采用了叫做Lloyd的放松算法,這是一種聚類算法,簡單來講就是經(jīng)過數(shù)次迭代,每次迭代將維諾圖的中心點移動到所在多邊形的中心,重新計算維諾圖。實際實驗后發(fā)現(xiàn)2次迭代已經(jīng)完全能達(dá)到效果。

數(shù)據(jù)結(jié)構(gòu)

源代碼中的VoronoiElement文件里保存了數(shù)據(jù)結(jié)構(gòu),大部分都具有注釋,需要解釋的Center類代表了維諾圖的中心點,也就是Delaunay三角網(wǎng)的頂點,而Corner類代表三角形的外心,也就是圖中多邊形的交點。這樣做的目的事實上產(chǎn)生了兩張圖的信息,三角圖 和多邊形圖,后期開發(fā)時三角圖可以用作尋路而多邊形圖主要用于渲染。

區(qū)分陸地和水

第二步我們要為我們的地圖添加大陸和水的區(qū)別,自然界中的地表水的形成是有很多因素的,然而我們模擬時只能采用隨機模擬。

原文中給出了很多模擬的思路,我才用了柏林噪聲的方式進(jìn)行模擬,首先為地圖上每一個Corner點計算一個Perlin噪聲值,設(shè)定一定的參數(shù)決定這個噪聲值是否能使這個Corner具有水的屬性。我才用的方式是比較(PerlinNoise)與(waterScale + waterScale * Distance(地圖中心,該點))大小,這樣既可以修改waterScale的參數(shù)值來修改生成地圖的水量多少,同時也確保在地圖邊緣更容易出現(xiàn)水,讓我們的地圖看起來像是一個海上的大陸(我默認(rèn)地圖邊緣都是水)。

在決定好Corner點的屬性后,我們把與水Corner相接的Center(也就是多邊形)記為水屬性,可以通過Corner的touches鏈表獲取到與其相接的Center。

區(qū)分水和大陸

區(qū)分海洋、內(nèi)陸湖、海岸線

首先我們規(guī)定與地圖邊界接壤的多邊形為海洋,所有與海洋接觸的水均為海洋,與海洋接觸的陸地是海岸線,其余的水為湖泊。

這里采用了一種種子填充的算法來計算。實際上這是一種廣度優(yōu)先搜素的策略,我們首先將地圖邊緣的Center添加到一個Queue隊列中,接下來執(zhí)行一個循環(huán),直到隊列中沒有剩余元素。循環(huán)時每次取出隊列的尾部Center,檢查與該Center直接接觸的臨近多邊形(這可以在neighbors鏈表中找到),若臨近Center為水且不為海洋,代表這是一個還未處理過的Center,將他置為海洋并加入隊列,若臨近Center不為水,則置為Coast并加入隊列。

在處理完全部的Center后,我們只需把海洋多邊形的Corner置為海洋,海岸線多邊形的Corner置為海岸線即可。

海岸線、海洋和湖泊

賦予高度

接下來我們?yōu)槲覀兊牡貓D添加高度的元素,讓我們的地形變得崎嶇。

為了地形隨機但是合理,我們采用了這樣一種策略:

靠近海岸線的地方更容易地勢低,島嶼中心的地方更容易地勢高,這樣是很合理的。

實現(xiàn)方案同樣是廣度優(yōu)先搜索,我們?yōu)槊恳粋€Corener定義了一個elevation的屬性,代表距離海岸線的最短距離,每經(jīng)過一個Corner,這個距離就加上一定的隨機值(這個隨機值范圍越大地形會變得越崎嶇)。

在搜索結(jié)束后,我們把每個Corner的elevation統(tǒng)一到0~1,作為這個點的高度,而每個Center的高度為多邊形所有corner高度的平均值。

需要注意的是,我們在統(tǒng)一Corner高度后需要進(jìn)行一個額外的操作,我們要計算每個Corner下降梯度最大的方向,這會為以后的河流生成做準(zhǔn)備。

高度

河流

在上一步時我們已經(jīng)計算好每個Corner的下降梯度最大的方向,這樣我們的河流生成就簡單了,我們只需要給出幾個參數(shù):河流的最小生成高度,河流的數(shù)量范圍。然后我們隨機給出河流的起點(確保大于最小高度,以及在陸地上),然后讓河流按梯度向下流淌,流過的每個Corener置為河流,直到流入湖泊或海洋,或者經(jīng)過一定路程

河流
  • 到這里我們的地圖已經(jīng)很好了,但是為了游戲性我們?nèi)匀粸槎噙呅翁砑觾蓚€特征

濕度和生物群落

我們?nèi)匀徊捎昧撕妥匀唤缦喾吹倪^程,我們通過多邊形到水源的距離來決定多邊形的濕度,計算的方案和高度計算基本一致。

濕度圖

有了濕度和高度后我們就可以模擬地形上的植物類型了,這里采用了這樣一張圖表來進(jìn)行模擬:

生物群落帶
群落分布

讀者可以自行修改數(shù)據(jù)讓地形變得更干燥或者濕潤

渲染

到目前為止我們都是用圖形化調(diào)試的方式展示,我們還需要渲染出3D網(wǎng)格。

我采用了Unity自帶的Mesh渲染的方式,為一個GameObject添加一個MeshRenderer和MeshFilter組件,修改mesh的頂點以及三角形屬性,把所有的Center和Corner都作為頂點,對于每個多邊形,讓Center頂點和每一條邊組成一個三角形網(wǎng)格(這里注意三角形要按順時針排序頂點,確保法線方向正確),同時為mesh分13個submesh(代表13種群落),根據(jù)多邊形的生物群落決定將三角形網(wǎng)格分配給哪個submesh(mesh.SetTriangles),為MeshRenderer分配13個材質(zhì),完成渲染。

demo

尾聲

目前不同群落的材質(zhì)我只用純色替代,以后會精致制作每一個材質(zhì),實現(xiàn)類似低多邊形渲染的效果。

且有很多噪聲、融合的計算還沒有做,如混合邊界等。

文中步驟的圖片都是一共500個多邊形,而最后的效果圖共有2000個多邊形,從編譯、啟動、計算到渲染完成一共耗時20s左右,實際游戲運行中計算耗時會更低,且完全沒有用到unity引擎的庫所以可以放在多線程中完成。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 前言 多邊形偏移 (polygon offset) 算法可能我們印象不深,不過用過 autoCAD 的同學(xué)應(yīng)該有印...
    zyl06閱讀 11,512評論 19 14
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,674評論 25 708
  • 等將來…… 等不忙…… 等下次…… 等有時間…… 等有條件…… 等有錢了…… 等來等去 …… 等沒了緣分…… 等沒...
    心羽暖姐姐閱讀 143評論 3 0