寫在前面的話 在996是福報,“付費上班”的如今。身為信息化進程的一顆螺絲釘,每天的通勤時間要四十幾分鐘(僅僅是在地鐵上哦),漫漫這時候回家路難免顯得有點寂寞有點空虛。這時好學的人會說聽聽有聲書,趁著下班時間提升自己。而我可要優雅的回應道:“老子搬了一天磚了,下班還不能享受享受了”。這不就迷上了各種 ...
寫在前面的話
在996是福報,“付費上班”的如今。身為信息化進程的一顆螺絲釘,每天的通勤時間要四十幾分鐘(僅僅是在地鐵上哦),漫漫這時候回家路難免顯得有點寂寞有點空虛。這時好學的人會說聽聽有聲書,趁著下班時間提升自己。而我可要優雅的回應道:“老子搬了一天磚了,下班還不能享受享受了”。這不就迷上了各種小游戲,什麼“種樹賺錢”,“鬥地主”,“消消樂”,這不就被小游戲給腐蝕了一心工作的 心靈。某團的方塊消消樂,對於小學生來說可能玩起來有點幼稚,但對於快三十的社畜來說玩的津津有味。在深入入坑之後,覺得這小游戲實現原理也是比較簡單的。
如下就是模擬完成之後的小游戲了。
游戲邏輯分析
1.業務邏輯
在下方的隨機生成不同形狀的三個元素,將其拖動到中間的操作面板中進行排列。如果排列的元素在中間的操作面板中滿足某行或者某列都排滿的情況下那麼這行上的元素就得到消除。在得道消除的同時記錄分數。記錄分數的形式包括按在面板排列的元素的單元格個數(諸如一個形狀為橫向的四個單元格組成的形狀,那麼成功在中間的操作面板上排列後分數相同的加上四分)。當下麵的元素麵板生成的元素無論如何都不能夠在中間的操作面板上排列下的時候就宣告游戲結束。
2.佈局分析
游戲界面總體分為上中下結構,上面為計算分數的面板(姑且命名為:積分面板),用於展示當前分數和歷史最高分數。中間為主體消除面板(姑且命名為:操作面板),用於容納各種元素。下麵的面板用於生成不同形狀的元素(姑且命名為:元素麵板)。
3.轉換為代碼語言
-
使用面向對象編程根據面板的佈局分為積分類(用於控制積分的統計),面板類(用於控制操作面板的排列消除和各類邏輯判斷)和元素類(用於生成隨機元素)。
-
元素的隨機生成使用二維數組的方式(諸如:[[0,1,0],[1,1,1]])第一維的數組表示行,第二維的元素表示這一行對應的列。列中的數字為1時表示有單元格,為0的時候表示沒有單元格。
-
面板也是使用二維數組來表示行跟列,那麼判斷面板是否被占滿的問題就可以轉換成二維數組的值問題了。
-
游戲最重要的除了規則就是操作了,該游戲細細分析起來有很多的操作需要轉換成各種事件。諸如元素的拖動,操作面板預展示可以排列的元素形狀等。但滿足可以消除某行或者某列的預展示等效果
核心邏輯實現
1.判斷操作面板剩下的單元格是否可以容納的下元素麵板的元素
/**
* 元素是否可以嵌入面板
* @param {*} twoArr 元素 二維數組[[1,1,1]]
* @returns 返回結果
*/
eleIsInlay(twoArr = [
[]
]) {
let res = false;
let eleHeight = twoArr.length;//獲取形狀占單元格的高度
let eleWidth = twoArr[0].length;//獲取形狀占單元格的寬度
// this.twoArray 二維數組面板 寬高都為2的面板表示為:[[0,0],[0,0]]
for (let rowIndex = 0; rowIndex <= this.twoArray.length - eleHeight; rowIndex++) {
let canvasRow = this.twoArray[rowIndex];
for (let colIndex = 0; colIndex <= canvasRow.length - eleWidth; colIndex++) {
let col = canvasRow[colIndex];//col 為單元格的值 值為1 表示改單元格已經被占滿
if (col !== 1) {
let _success_num = 0;
//mapPosOfCanvas 方法獲取形狀在面板中對應的位置,返回每個元素對應操作面板的位置數值
let _mapArr = this.mapPosOfCanvas(twoArr, rowIndex, colIndex);
for (let arrIndex = 0; arrIndex < _mapArr.length; arrIndex++) {
let _mapCell = _mapArr[arrIndex];
let _mRow = _mapCell[0];
let _mCol = _mapCell[1];
if (this.twoArray[_mRow][_mCol] == 1)
break;
_success_num++;//記錄沒有被占滿位置的單元格個數
}
if (_success_num == _mapArr.length) {//都沒暫滿則表示該位置可以放的下該形狀元素
res = true;
break;
}
}
if (res)
break;
}
}
return res;
}
判斷邏輯概括:
將對應的形狀元素的二維數組轉換成面板中對應的位置(同樣用二維數組表示),遍歷面板的位置,對每個位置中的單元格和轉換成面板位置的形狀二維數組進行比較,如果有對應位置的單元格可以放的下轉換之後的形狀二維數組則表示該面板還能放的下該形狀,則游戲還能繼續。
2.判定拖動的元素是否可以排列到操作面板中
"mousemove": function (evt) {
if ((evt.clientX <= _maxLeft) &&
(evt.clientX >= _minLeft) &&
(evt.clientY <= _maxTop) &&
(evt.clientY >= _minTop)) {//判斷形狀的位置是否在面板裡面了
// compDistance 方法根據位置獲取面板中對應離元素最近的單元格
let _near_cell = canvasModel.compDistance(
(evt.clientX - _width_half),
(evt.clientY - _height_half));
let _cur_shape = _curHoverShapeArr.shape.shape;
let _cur_shape_color = _curHoverShapeArr.color;
_cell_row = Number(_near_cell.row);
_cell_col = Number(_near_cell.col);
// mapPosOfCanvas 方法 將形狀 在面板中的 對應的行(_cell_row)和列(_cell_col)的具體位置用二維數組表示
_shapesMapcanvas = canvasModel.mapPosOfCanvas(_cur_shape, _cell_row, _cell_col);
canvasModel.resetPreEle();// 移除上一個預覽元素
//isInlay 判斷轉換成面板的二維數組的位置 是否可以嵌入到面板中
if (canvasModel.isInlay(_shapesMapcanvas)) { //顯示預覽
canvasModel.setPreEle(_shapesMapcanvas, _cur_shape_color);//設置預覽元素
//........
}
} else {
_shapesMapcanvas = [];
canvasModel.removeTranCell()
canvasModel.resetPreEle();// 移除之前設置的預覽元素
}
},
判斷邏輯概括:
在拖動形狀元素的時候對形狀元素的移動事件進行綁定,在綁定事件裡面先判斷元素的位置是否在面板的位置裡面,在裡面的時候繼續判斷得出離形狀元素最近的單元格是哪一個,根據最近的單元格判斷出該元素能不能嵌入到面板中,如果能嵌入到 面板中則在面板的嵌入位置展示預嵌入的單元格。
總結
該小游戲是一個對數組應用的的一個典型案例,其中還包含了面向編程的思想和jquery事件的使用,如果還想進一步優化的話其實還可以加入音效的控制,比如說消除之後的音效,排不下元素時的失敗音效。
微信公公眾號 搜索 “不正經小前端” 並關註,和我一起學習海量前端技術本文來自博客園,作者:七分暖,轉載請註明原文鏈接:https://www.cnblogs.com/lin494910940/p/17578557.html