1. 程式人生 > >【小白學遊戲常用演算法】一、隨機迷宮演算法

【小白學遊戲常用演算法】一、隨機迷宮演算法

 現在的很多遊戲中的地圖一般採用格子的方式,雖然在表面地圖上無法看到實際的格子,但是在地圖的結構中專門有一個邏輯層,這個層和地圖大小相等,劃出很多小的格子,然後在可以通過的地方使用0表示,在有障礙的且不能通過的地方用1或者其他數字表示(如圖所示)。有了這個邏輯層之後,實際上自動尋路就轉換成了如何在一個二維陣列中找出一條從邏輯值為0的地點移動到目標的路徑。在尋路之前,我們首先要隨機生成這些地圖。

                                                                           

    

                             遊戲中地圖      二維陣列邏輯層

  本質上,地圖的障礙邏輯層是由一個二維陣列儲存的。障礙標記在二維陣列中的資料值以0或者1表示,我們首先需要做的就是隨機產生這樣的二維陣列。當然,最簡單的辦法就是迴圈這個二維陣列,然後在每一個位置隨機地產生0或者1,但是這種演算法產生的圖形比較難看,並且不一定保證圖中的任意兩點可以相連通。

  在隨機生成的迷宮中要求任意兩點,都可以找到一條路徑相通,所以在圖論中可以認為迷宮就是一個連通圖。產生連通圖的常見方法有克魯斯卡爾和普利姆演算法,這裡我們以普利姆演算法為例實現一下,使用普利姆演算法產生的迷宮比較自然和隨機。

                           

  (1)如上圖所示為一個6x6的迷宮,先假設迷宮中所有的通路都是完全封閉的,黃色的格子表示可以通過,黑色的格子表示牆壁或者障礙不能通過。

  (2)隨機選擇一個黃色的格子作為當前正在訪問的格子,同時把該格子放入一個已經訪問的列表中。

  (3)迴圈以下操作,直到所有的格子都被訪問到。

     1.得到當前訪問格子的四周(上下左右)的格子,在這些格子中隨機選擇一個沒有在訪問列表中的格子,如果找到,則把該格子和當前訪問的格子中間的牆打通(置為0),把該格子作為當前訪問的格子,並放入訪問列表。

     2.如果周圍所有的格子都已經訪問過,則從已訪問的列表中,隨機選取一個作為當前訪問的格子。

   通過以上的迷宮生成演算法,可以生成一個自然隨機的迷宮、

  下面使用程式碼實現一個R行N列大小的隨機迷宮,R行表示的是剛開始空白格子的行數,而格子之間還有牆壁和障礙物,所以最終產生的二維陣列大小實際為2R+1 * 2N+1

複製程式碼

1 //產生隨機迷宮
 2 primMaze:function(r,c)
 3 {
 4          //初始化陣列
 5          function init(r,c)
 6          {
 7              var a = new Array(2*r+1);
 8             //全部置1
 9             for(var i=0,len=a.length;i<len;i++)
10             {
11                 var cols = 2*c+1;
12                 a[i]= new Array(cols);
13                 ArrayUtil.fillWith(a[i],1);
14             }
15             //中間格子為0
16             for(var i=0;i<r;i++)
17                 for(var j=0;j<c;j++)
18                 {
19                     a[2*i+1][2*j+1] = 0;
20                 }
21                 return a;
22         }
23          //處理陣列,產生最終的陣列
24          function process(arr)
25          {
26            //acc存放已訪問佇列,noacc存放沒有訪問佇列
27            var acc = [],noacc = [];
28            var r = arr.length>>1,c=arr[0].length>>1;
29            var count = r*c;
30            for(var i=0;i<count;i++){noacc[i]=0;}
31            //定義空單元上下左右偏移
32            var offs=[-c,c,-1,1],offR=[-1,1,0,0],offC=[0,0,-1,1];      
33            //隨機從noacc取出一個位置
34            var pos = MathUtil.randInt(count);
35            noacc[pos]=1;
36            acc.push(pos);       
37            while(acc.length<count)
38            {        
39                var ls = -1,offPos = -1;
40                offPos = -1;
41              //找出pos位置在二維陣列中的座標
42              var pr = pos/c|0,pc=pos%c,co=0,o=0;
43              //隨機取上下左右四個單元
44              while(++co<5)
45              {
46                  o = MathUtil.randInt(0,5);
47                  ls =offs[o]+pos;
48                  var tpr = pr+offR[o];
49                  var tpc = pc+offC[o];           
50                  if(tpr>=0&&tpc>=0&&tpr<=r-1&&tpc<=c-1&&noacc[ls]==0){ offPos = o;break;}           
51              }                 
52              if(offPos<0)
53              {
54 
55                  pos = acc[MathUtil.randInt(acc.length)];
56              }
57              else
58              {
59                  pr = 2*pr+1;
60                  pc = 2*pc+1;
61                 //相鄰空單元中間的位置置0
62                 arr[pr+offR[offPos]][pc+offC[offPos]]=0;
63                 pos = ls;  
64                 noacc[pos] = 1;
65                 acc.push(pos);
66             }        
67         }
68     }
69     var a = init(r,c);
70     process(a);
71     return a;
72 }

複製程式碼

利用上面的演算法我們就可以實現一個類似於下面的隨機迷宮了。

有了隨機迷宮就得開始尋路了,下一篇的部落格中我們將一起學習一下最常見的A*尋路演算法。