web版掃雷小遊戲(三)

~~~接上篇,上篇介紹了遊戲實現過程當中第一個比較繁瑣的地方,如今展示在玩家面前的是一個有血有肉的棋盤,從某種意義上說玩家已經能夠開始遊戲了,可是不夠人性化,玩家只能一個一個節點的點開,而後判斷,而後標記等等,程序暫不能人性化的輔助玩家將遊戲進行下去,趣味性不夠強,接下來就來完善這些輔助功能。函數

2、空節點點擊觸發其周圍全部空節點展開spa

 此過程主要是輔助玩家快速標記雷點,加強遊戲趣味性,即當遊戲進行中,玩家點擊棋盤中某個未展開的節點M時,若是該節點是空節點(對應的數值爲0),則展開該節點的同時,須要遍歷其周圍8個方向上的節點,若是某個方向上的節點也是空節點,則再以該點爲中心重複上述過程,直到遍歷完全部相鄰節點M的空節點爲止。這個過程值得注意的是:code

1. 遊戲開始後(玩家第一次點擊),若是第一個節點是非雷點,則程序開始監聽空節點點擊事件。 對象

2. 節點M是空節點,即其周圍沒有雷點存在,用戶點擊後該點周圍8個方向上的節點均須要展開。blog

3. 遍歷節點M周圍節點時,若是遇到新的空節點,則當即以該節點爲中心重複遍歷過程,此處咱們採用遞歸處理,默認從北方節點順時針方向依次遍歷。遞歸

4. 遞歸遍歷時,節點M與其周圍節點互爲方位節點,爲了不陷入重複循環,提升效率,則過濾掉已經遍歷過的節點,這裏定義一個棧,壓入已經遍歷過的節點,每次進入遞歸時判斷節點是否已經在棧中,若是在,則忽略。遊戲

5. 遍歷時,若是節點非空(必定不是雷點),則只需展開便可,接着判斷並遍歷下一個方位節點。事件

6. 一次遞歸完成後,程序會自動清除或重置當前次遞歸遍歷產生的臨時數據和變量,開始監聽下一次空節點的點擊事件。圖片

功能效果及遍歷路徑見下例:io

如上圖,節點M點擊後,程序會按照黃色箭頭(只畫出部分)的順序依次遍歷並展開節點,直到由M節點觸發的全部相鄰的空節點遍歷完畢,則遞歸完成。

這個過程須要在棋盤控制類(BombObjectList)中實現:

首先,須要定義兩個對外的類的屬性,標記爲BombObjectList.fire_XBombObjectList.fire_X,這兩個屬性默認初始化值爲-1,一旦玩家觸發了空節點,則分別賦值爲空節點在棋盤中的x、y座標。

其次,在類中定義一個定時器,隨時監聽上面兩個屬性值的變化,一旦值均不爲-1,表示玩家觸發了空節點的點擊事件,程序須要響應這個事件,咱們定義一個響應函數,標記爲CheckAroundBomb

最後,遍歷該空節點,必要時進行遞歸,遞歸過程見上述描述,遞歸函數標記爲checkBomb

類的對外屬性定義以下:

BombObjectList.fire_X = -1;         //觸發空節點彈開時節點的x座標
BombObjectList.fire_Y = -1;         //觸發空節點彈開時節點的y座標

定時器定義以下(黑色陰影部分爲空節點點擊事件監聽過程),50毫秒監聽一次:

 1 //開啓對空節點的監聽
 2     var ListenFire = null;
 3     me.ListenKong = function() {
 4         if (ListenFire === null || ListenFire === undefined) {
 5             ListenFire = setInterval(function() {
 6                 if (BombObjectList.fireFlag === 2) {
 7                     //遊戲結束,關閉定時器
 8                     clearInterval(ListenFire);
 9                     ListenFire = null;
10                 }
11                 else if (BombObjectList.fireFlag === 1) {
12                     //遊戲進行中,檢測空節點的點擊事件
13                     if (BombObjectList.fire_X !== -1 && BombObjectList.fire_Y !== -1) { 14                         //根據x、y座標找到該節點在節點集合中的對象
15                         var tempObjEx = me.CheckObjItem(BombObjectList.fire_X, BombObjectList.fire_Y).obj; 16                         if (tempObjEx !== null) { 17  me.CheckAroundBomb(tempObjEx); 18  } 19  } 20                     //遊戲進行中,監測雙擊事件
21                     if (BombObjectList.DC_X !== -1 && BombObjectList.DC_Y !== -1) {
22                         //根據x、y座標找到該節點在節點集合中的對象
23                         var tempObjEx = me.CheckObjItem(BombObjectList.DC_X, BombObjectList.DC_Y).obj;
24                         if (tempObjEx !== null) {
25                             if (BombImgObject.MouseType === 3) {
26                                 //雙擊按下事件
27                                 me.CheckAroundBombDC_Down(tempObjEx);
28                             }
29                             else {
30                                 //雙擊彈起事件
31                                 me.CheckAroundBombDC_Up(tempObjEx);
32                             }
33                         } 
34                     }
35                 }
36             }, 50);

事件響應函數CheckAroundBomb和遞歸函數定義以下:

 1 //遞歸函數
 2     function checkBomb(obj) {
 3         //對當前空區進行八方位踩點,遞歸查詢相連的全部空區,並所有打開
 4         for (var i = 0; i < me.enmbVal.length; i++) {
 5             var _Obj = eval("obj." + me.enmbVal[i]);
 6             //判斷該方位是否存在,存在則展開節點
 7             if (_Obj != null) {
 8                 var _X = _Obj.X;
 9                 var _Y = _Obj.Y;
10                 var tempObjEx = me.CheckObjItem(_X, _Y).obj;
11                 if (tempObjEx.DisplayNum === 0) {
12                     //若是爲空,遞歸查詢空節點,並展開其8個方位的全部節點
13                     tempObjEx.ImgObj.ShowNumImg();
14                     //將當前節點入棧,以避免後續再次將其做爲空節點進入遞歸流程
15                     var isIn = (function() {
16                         for (var s = 0; s < stackObj.length; s++) {
17                             if (stackObj[s].Equals(tempObjEx)) {
18                                 return true;
19                             }
20                         }
21                         return false;
22                     } ());
23                     if (!isIn) {
24                         stackObj.push(tempObjEx);
25                         //進入遞歸流程
26                         checkBomb(tempObjEx);
27                     }
28                 }
29                 else {
30                     tempObjEx.ImgObj.ShowNumImg();
31                 }
32             }
33         }
34     }
35     //存儲遍歷過的節點
36     var stackObj = [];
37     me.CheckAroundBomb = function(that) {
38         if (!(that instanceof BombObject) || that.constructor !== BombObject || that == null) {
39             throw new Error("the obj is not allowed.");
40             return;
41         }
42         else {
43             stackObj.push(that);
44             //進入遞歸調度,查找並展開8個方向上的節點
45             checkBomb(that);
46             //重置觸自動發展示時監聽的節點座標,準備監聽下一個
47             BombObjectList.fire_X = -1;
48             BombObjectList.fire_Y = -1;
49             //清空遍歷過的堆棧對象列表
50             stackObj.length = 0;
51         }
52     };

 3、鼠標左鍵和右鍵一塊兒按下時的事件響應

這個過程是這三個裏面最繁瑣的一個,不過它的實現能夠大大加強遊戲的趣味性,在掃雷遊戲過程當中,這個功能是用的最頻繁的,對腳本程序的檢測和判斷及效率要求較高,整體來講,主要基於節點數值和其周圍雷點個數一直的原則進行判斷,分析得出存在如下幾種狀況:

1. 玩家雙擊的對象是未展開的節點,此時系統並不須要過多的判斷,由於玩家尚未標記出該點周圍的雷點,程序只需閃爍提示該點周圍的節點便可,鼠標事件後復原到按下前的狀態。

2. 玩家雙擊的對象是已展開的節點,此時有兩種狀況,好比改節點的數值爲N,而玩家在其周圍標記的雷點個數爲M:

  若是N!=M,則提示用戶進行修改,閃爍提示同上一條;

  若是M=N,接下來要判斷該點周圍的未標記節點的類型,記未標記雷點的個數爲C,若C>0,則用戶標記錯誤,鼠標按下後遊戲結束,提示用戶標記錯誤;若C=0,則用戶標記正確,展開全部周圍未標記節點,當這些節點中有空節點時,則須要遞歸展示全部相鄰的空節點,過程同上述處理過程。

上述幾種狀況的示例見下圖:

對於這一過程的實現,咱們分兩步進行:

第一步:響應鼠標左右鍵一塊兒按下事件,函數標記爲CheckAroundBombDC_Down,此過程記錄點擊節點周圍的遊戲上下文,如該點周圍已經標記的雷點個數markRoundNum,未標記節點中是否有雷點標記位hasBomb。

第二步:響應鼠標左右鍵一塊兒提起事件,函數標記爲CheckAroundBombDC_Up,若是該點未展開,則復原圖片對象,什麼也不作;若是該點已展開,若其周圍有雷點,則復原圖片,什麼也不作,不然展開其周圍的非雷點,必要時進行遞歸展示。

此外,棋盤控制類中須要定義個定時器,隨時監聽左右鍵一塊兒按下事件,並將事件觸發對象節點的x、y座標記錄到棋盤類的兩個對外屬性BombObjectList.DC_XBombObjectList.DC_Y,就像監聽空節點點擊事件同樣。

屬性定義以下:

1 BombObjectList.DC_X = -1;           //左右鍵一塊兒按下時節點的x座標
2 BombObjectList.DC_Y = -1;           //左右鍵一塊兒按下時節點的y座標

 定時器定義以下(黑色陰影部分爲空節點點擊事件監聽過程),50毫秒監聽一次:

 1 //開啓對空節點的監聽
 2     var ListenFire = null;
 3     me.ListenKong = function() {
 4         if (ListenFire === null || ListenFire === undefined) {
 5             ListenFire = setInterval(function() {
 6                 if (BombObjectList.fireFlag === 2) {
 7                     //遊戲結束,關閉定時器
 8                     clearInterval(ListenFire);
 9                     ListenFire = null;
10                 }
11                 else if (BombObjectList.fireFlag === 1) {
12                     //遊戲進行中,檢測空節點的點擊事件
13                     if (BombObjectList.fire_X !== -1 && BombObjectList.fire_Y !== -1) {
14                         //根據x、y座標找到該節點在節點集合中的對象
15                         var tempObjEx = me.CheckObjItem(BombObjectList.fire_X, BombObjectList.fire_Y).obj;
16                         if (tempObjEx !== null) {
17                             me.CheckAroundBomb(tempObjEx);
18                         }
19                     }
20                     //遊戲進行中,監測雙擊事件
21                     if (BombObjectList.DC_X !== -1 && BombObjectList.DC_Y !== -1) { 22                         //根據x、y座標找到該節點在節點集合中的對象
23                         var tempObjEx = me.CheckObjItem(BombObjectList.DC_X, BombObjectList.DC_Y).obj; 24                         if (tempObjEx !== null) { 25                             if (BombImgObject.MouseType === 3) { 26                                 //雙擊按下事件
27  me.CheckAroundBombDC_Down(tempObjEx); 28  } 29                             else { 30                                 //雙擊彈起事件
31  me.CheckAroundBombDC_Up(tempObjEx); 32  } 33  } 34  } 35                 }
36             }, 50);

 鼠標左右鍵一塊兒按下和彈起事件定義以下:

 1 var tempAroundObj = []; //存儲雙擊時遍歷的8方位節點對象
 2     var hasBomb = false;    //標記上一變量存儲的對象是否包含雷點
 3     //雙擊按下時事件處理程序
 4     me.CheckAroundBombDC_Down = function(obj) {
 5         if (!(obj instanceof BombObject) || obj.constructor !== BombObject || obj == null) {
 6             throw new Error("the obj is not allowed.");
 7             return;
 8         }
 9         else {
10             var markRoundNum = 0;
11             for (var i = 0; i < me.enmbVal.length; i++) {
12                 var _Obj = eval("obj." + me.enmbVal[i]);
13                 //判斷該方位是否存在,存在則展開節點
14                 if (_Obj != null) {
15                     var _X = _Obj.X;
16                     var _Y = _Obj.Y;
17                     var tempObjEx = me.CheckObjItem(_X, _Y).obj;
18                     var tempImgPic = tempObjEx.ImgObj;
19                     if (!tempImgPic.ImgObj.flag) {
20                         //該節點還沒有展開,此時需更改該圖片
21                         tempImgPic.ShowNumImg(1);
22                         tempAroundObj.push(tempObjEx);
23                         //若是是雷點,且未標記
24                         if (tempObjEx.IsBomb && tempImgPic.ImgObj.src.indexOf("flag") < 0) hasBomb = true;
25                         //統計核心點旁邊被標記的雷點個數
26                         if (tempObjEx.IsBomb && tempImgPic.ImgObj.src.indexOf("flag") >= 0) markRoundNum++;
27                     }
28                 }
29             }
30             //判斷本身
31             if (!obj.ImgObj.ImgObj.flag) {
32                 //該節點還沒有展開,此時需更改該圖片
33                 obj.ImgObj.ShowNumImg(1);
34                 tempAroundObj.push(obj);
35             }
36             else {
37                 //若是核心點是展開的,則須要比較核心點的數值和其周圍已經標記處的雷點個數
38                 if ((markRoundNum === parseInt(obj.DisplayNum)) && hasBomb) {
39                     //標記錯誤,遊戲結束
40                     hasBomb = false;
41                 }
42                 else { 
43                     //容許用戶修改,什麼也不作
44                 }
45             }
46         }
47     }
48     //雙擊彈起時事件處理程序,檢測當前點周圍的節點,必要時進行遞歸
49     me.CheckAroundBombDC_Up = function(obj) {
50         if (!(obj instanceof BombObject) || obj.constructor !== BombObject || obj == null) {
51             throw new Error("the obj is not allowed.");
52             return;
53         }
54         else {
55             //若是核心節點是未展開的或者遍歷列表中包含雷點,則復原遍歷列表中對象對應的圖片對象
56             if (!obj.ImgObj.ImgObj.flag || hasBomb) {
57                 for (var i = 0; i < tempAroundObj.length; i++) {
58                     //復原節點圖片
59                     tempAroundObj[i].ImgObj.ShowNumImg(2);
60                 }
61             }
62             else {
63                 //若是核心點是展開的,沒有雷點,則自動展開,空節點須要遞歸
64                 for (var j = 0; j < tempAroundObj.length; j++) {
65                     //展開節點
66                     tempAroundObj[j].ImgObj.ShowNumImg();
67                     //若是節點爲空,則須要遞歸遍歷展開
68                     if (tempAroundObj[j].DisplayNum === 0) {
69                         checkBomb(tempAroundObj[j]);
70                     }
71                 }
72             }
73             //復原監聽變量
74             BombObjectList.DC_X = -1;
75             BombObjectList.DC_Y = -1;
76             //刪除遍歷的臨時節點對象列表
77             tempAroundObj.length = 0;
78             hasBomb = false;
79         }
80     }

 至此,遊戲的三個難點基本解決,看到這裏,還有兩個東西沒有介紹,一個是雷點類(即節點信息類)BombObject,另外一個是節點對應的圖片對象類BombImgObject,下篇將揭開其神祕的面紗。

接下篇~~~~

相關文章
相關標籤/搜索