現在的很多游戲中的地圖一般采用格子的方式,雖然在表面地圖上無法看到實際的格子,但是在地圖的結構中專門有一個邏輯層,這個層和地圖大小相等,划出很多小的格子,然后在可以通過的地方使用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*尋路算法。