隨機迷宮生成演算法整理分析

2021-08-19 18:38:06 字數 3634 閱讀 3821

蒐集整理了一些遊戲迷宮生成的演算法與實現

前段時間學校遊戲開發課大作業,做了乙個roguelike的恐怖遊戲。蒐集整理了一些迷宮生成的演算法。

當初也受了indienova上一些文章的啟發。現在在此把學到的一些東西理一理分享出來。

第一次寫這種東西,感覺有點囉嗦,還請大家不要介意,也可以直接看專案位址

**寫在unity環境下,應該可以直接使用。

先上一張圖

這是我最早拍腦袋憑著感覺寫的乙個演算法結果,給定區域長寬和分支概率,可以生成一張迷宮圖。

這完全就是隨機挖洞**,其步驟如下:

計算當前掃瞄點周圍可以挖的方塊

隨機選乙個方塊挖開

若周圍還有可挖方塊,按分支概率隨機挖開另一方塊,設為新掃瞄點

所有掃瞄點執行1操作

若周圍無方塊可挖,中止此掃瞄點工作。

可以看出,這個演算法有相當的缺陷,生成的迷宮總面積不可控,在運氣不好的極端情況下,會產生比預期面積小很多的迷宮。

即使我們將分支概率調到100%,依舊會有黑色的空洞存在:

而且生成的迷宮非常扭曲怪誕,這很克蘇魯。或許我們可以風格化一下……
此時的迷宮已經勉強可以使用,但是與傳統迷宮的差別依舊非常大。

它的斜線非常多。這會使得遊戲過程中包含八個方向,對玩家的方向感是極大的考驗,很難再記住地圖,容易暈頭轉向。

對於這個演算法,相比室內環境,更適合生成自然環境下迷宮。也可以作為無主線、弱主線沙盤遊戲的大地圖生成的一環。

接下來這個演算法與第乙個就是兩個極端——生成完全沒有斜線的迷宮。

話不多說,先上圖:

在介紹本演算法前,需要提出乙個概念

完美迷宮perfect maze:沒有迴路,也沒有孤立區域的迷宮。用圖論來解釋,就是可以用生成樹表示的迷宮,迷宮中兩點有且僅有一條路徑。

這個演算法是乙個分治演算法,即將一塊大的生成區域分成4塊小區域分別生成迷宮並保證聯通,以此類推,直到不可細分。

分塊很簡單,長寬上各取乙個隨機數即可。如何保證迷宮完美呢?

我們看極端情況,對於乙個田字形區域,生成完美迷宮的方法是敲開三堵牆。

利用分治演算法的特性,每一層遞迴都是完美迷宮,直到全圖生成完美迷宮。

演算法不難,注意遞迴狀態的邊界情況就行。

這種分治遞迴的痕跡在生成的地圖俯檢視上很明顯,但對於置身其中的玩家或許就不是了。

它生成的迷宮完全沒有斜線,橫平豎直,同時會生成4*4的小房間。

用作城市地圖、或建築環境的迷宮非常合適。

當然在遊戲中地圖沒有迴路是非常致命的,乙個完美迷宮會讓玩家疲於奔命,並不方便設計玩法。

對於迴路,我們只需要消除死路就行了,也就是那些三面臨牆的格仔,在地圖生成完後遍歷死路,按一定概率打通即可。

絕大多數的程式設計問題都可以用數學工具解決,當然我們的迷宮生成演算法也不例外。

數學中最適合表達迷宮的符號莫過於,下面兩個演算法是迷宮生成中應用最普遍的理論之二。

首先我們需要將地圖轉換為便於數學表達的形式。

之前兩個演算法在處理地圖時都是以方塊為單位的,即每乙個方塊不是牆就是路。

的基本組成是,對於乙個待處理的迷宮,我們做如下轉換。

迷宮大小10*10,其中白塊代表,紅塊代表,而黑塊代表虛無,只是填充物質罷了。

如果乙個中,任意兩都能通過組成的路徑聯通,稱之為連通圖

而如果乙個連通圖上沒有迴路,則我們可以稱之為,因為沒有迴路,所以每對點之間有且僅有一條路徑聯通。

可以看到,與我們完美迷宮的概念不謀而合,所以現在我們的任務是找到包含所有點的一棵

生成樹,顧名思義,就是從給定的,集合中生成一棵符合要求的樹。

下面介紹的兩種最小生成樹演算法都可以勝任。

雖然寫作最小生成樹,但這兩個演算法其實可以做到「按一定條件生成樹」。

「最小」是演算法的典型描述,即在有權邊的集合中找出權值最小的樹。原演算法使用貪心演算法求解。

而在這裡,我們的條件就是:隨機。

下面簡單介紹一下這兩個演算法的步驟:

兩個演算法都需要的集合e,與的集合v。對於上圖,e代表所有白塊,v代表所有紅塊

kruskal:

一開始每個點將自己作為單獨的一棵樹。

v中隨機選出一條邊v判斷v兩端的e1e2是否屬於一棵生成樹

v中刪除vv不為空,則返回 1. ,v為空則完成

ps:判斷與合併兩點所在樹可以使用並查集相關演算法,在專案**中ufset類就是並查集的c#實現之一

這裡簡單講下並查集,並查集是指一些不相交集合的 合併 與 查詢 問題,

對應到我們迷宮問題中就是:合併不相連的生成樹、查詢兩點是否屬於同乙個生成樹。

並查集使用了一種稱為 路徑壓縮 的演算法,使得所有子節點的父節點均指向跟根節點。

雖然壓縮演算法是為了提高合併效率,但壓縮演算法本身時間複雜度也是o(n),所以我們只在查詢乙個節點時,才對此節點所在路徑進行壓縮,並且在合併時,將小樹併入大樹,以平衡效率。

經過優化的並查集合並演算法時間複雜度可達神奇的常數級,比起之前的全圖標記不知道高到**去了,證明就在此略過,有興趣的同學可以去深入學習一下。

prim:初始v為空,所有ee標記為0

隨機選乙個點e將與e相連的邊的集合併入入v,e標記為1

v中隨機選一條邊v判斷v兩端情況

v中刪除v當所有ee均被標記為1,結束,否則返回 3. 。

ps:可以維護乙個包含所有vv的標記表,防止被重複併入v,提高效率

以上為演算法步驟,建議配合**食用更佳。

下為執行結果

前者為kruskal,後者為prim。白路黑牆。

可以看到兩個演算法的生成迷宮風格幾乎一樣,並且都較為接近傳統迷宮。可以用於各類需要迷宮生成的遊戲。

值得一提的是這兩個演算法都可以加入房間,只需將房間預先生成,在將剩餘可生成的點與邊的集合放入演算法中執行即可。

關於這個詳細可以參考房間和迷宮:乙個地牢生成演算法

到這裡關於遊戲中迷宮生成最常用的幾個演算法已經寫完了。除了上述幾種以外,迷宮的生成方法還深度廣度優先搜尋之類很多。

此外還有諸多適用於特定遊戲系統的地圖生成演算法,如mc中的噪音,unexplored中的環狀地圖等

**:

隨機系列生成演算法

給定乙個正整數n,需要輸出乙個長度為n的陣列,陣列元素是隨機數,範圍為0 n 1,且元素不能重複。比如 n 3 時,需要獲取乙個長度為3的陣列,元素範圍為0 2。首先,讓我們先構造如下函式 cpp view plain copy 隨機數生成函式,生成 a,b 之間的乙個隨機數字 其中0 aint r...

演算法 隨機生成迷宮

演算法原理 從起點開始,隨機選擇乙個方向移動,一直移動到終點,則移動的路徑便是迷宮的路徑。移動過程中要保證路徑不要相交,不要超出邊界,生成效果 public partial class mainform form void btncreateclick object sender,eventargs...

隨機數生成演算法

看到一些介紹隨機數生成演算法的文章,收集下來,有空深入了解下。c語言中偽隨機數生成演算法實際上是採用了 線性同餘法 具體的計算如下 xi xi 1 a c mod m 其中a,c,m都是常數 一般會取質數 當c 0時,叫做乘同餘法。srand 函式置的seed實際上會作為x0被代入上式中,然後每次呼...