最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Cocos掃雷游戲核心算法思想

2021-11-08 12:10 作者:unity小能手  | 我要投稿

一、?掃雷游戲?qū)崿F(xiàn)核心思路解析

數(shù)據(jù)和視圖盡量分離。采用面向?qū)ο蟮膶?shí)現(xiàn)設(shè)計(jì)數(shù)據(jù)模塊。格子作為一類對(duì)象,雷場(chǎng)作為一類對(duì)象,雷場(chǎng)由格子構(gòu)成。

二、?掃雷游戲核心數(shù)據(jù)模塊

1. Cell.js單元格類

// 單個(gè)單元格,用于保存數(shù)據(jù)
// x,y,坐標(biāo),或者行和列,
//?info顯示文本,空表示什么都沒(méi)有,周邊也沒(méi)有地雷
// *表示此處是一顆地雷,數(shù)字表示其周邊有幾顆地雷
function Cell(x, y, info){
this.x = x;
this.y = y;
this.info?= info;
};
module.exports = Cell;

2. MineField.js雷場(chǎng)類

// 雷場(chǎng)(行數(shù),列數(shù),地雷數(shù))
function MineField(rowNum, colNum, mineNum){
this.rowNum = rowNum;
this.colNum = colNum;
this.mineNum = mineNum;
// 調(diào)用【1】畫格子
this.init();
// 調(diào)用【2】藏地雷
this.hideMine();
// 調(diào)用【3】留暗號(hào)
this.markNumber();
};

module.exports = MineField;

【1】畫格子

雷場(chǎng)由Cell的對(duì)象構(gòu)成的數(shù)組組成,實(shí)質(zhì)就是給雷場(chǎng)的cells數(shù)組賦值。

// 【1】利用原型擴(kuò)展方法:初始化雷場(chǎng)的所有格子數(shù)據(jù)
MineField.prototype.init = function(){
let cellsNum = this.rowNum * this.colNum;
this.cells = new Array(cellsNum);
for(let i=0; i<this.rowNum; i++){
for(let j=0; j<this.colNum; j++){
let index = this.getIndexByXY(i,j);//this.colNum * i + j;
this.cells[index] = new Cell(i, j, "");
}
}
};

補(bǔ)充提煉通過(guò)坐標(biāo)獲取格子索引的方法,以供后續(xù)其它地方用:

// 【1-1】通過(guò)坐標(biāo)獲取單元格的所處格子的索引
MineField.prototype.getIndexByXY = function(x, y){
return x * this.colNum + y;
}
// 通過(guò)單元格對(duì)象獲取單元格所處格子的索引
MineField.prototype.getIndexByCell = function(cell){
return this.getIndexByXY(cell.x, cell.y);
}

在此文件頭部引入Cell類:

var Cell = require("Cell");

【2】藏地雷

實(shí)質(zhì)就是修改雷場(chǎng)的cells數(shù)組中的隨機(jī)一些索引的cell的info屬性值。

// 【2】藏地雷:將地雷數(shù)據(jù)設(shè)置到this.cells中的Cell的info中去
MineField.prototype.hideMine = function(){
// 隨機(jī)無(wú)重復(fù)元素的數(shù)組,且范圍限定在[0,this.rowNum*this.colNum);
let end = this.colNum * this.rowNum;
// 記錄所有地雷所在的cells的索引
this.mineIndexs = ArrayUtils.randChoiseFromTo(0, end, this.mineNum);

console.log("地雷位置序號(hào):",this.mineIndexs);

// 找到相應(yīng)格子的位置,設(shè)置其cell對(duì)象的info屬性為*,表示地雷
this.mineCells = new Array(this.mineNum);
for(let i=0,len=this.mineIndexs.length; i<len; i++){
let index = this.mineIndexs[i];
let cell = this.cells[index];
cell.info?= "*";
// 保存所有地雷所在的單元格
this.mineCells[i] = cell;
}
};

方法randChoiseFromTo參考四、ArrayUtils.js數(shù)組工具類。

在此文件頭部引入數(shù)組工具類:

var ArrayUtils = require("ArrayUtils");

【3】留暗號(hào)

實(shí)質(zhì)就是在地雷周邊8個(gè)格子中標(biāo)上數(shù)字,數(shù)值為此單元格周邊8個(gè)單元格中雷的數(shù)量。如下圖所示:

// 【3】留暗號(hào):標(biāo)記地雷周圍所有單元格的數(shù)字,也就是設(shè)置其info屬性
MineField.prototype.markNumber = function(){
// 遍歷所有地雷單元格,每次找到其周邊非雷格子,給其數(shù)字加1
console.log("this.mineCells:",this.mineCells)
for(let i=0,len=this.mineCells.length; i<len; i++){
// 【3-1】拿到地雷單元格 周圍的所有的非雷單元格(應(yīng)該是數(shù)字的單元格)
let numberCells = this.getNumberCellsAround(this.mineCells[i]);
// 【3-2】更新地雷周圍所有非雷(數(shù)字)單元格的數(shù)字標(biāo)記
this.updateNumberMarks(numberCells);
}
};

【3-1】獲取某顆地雷周圍所有單元格

如下圖所示,假如要獲?。?,0)周圍8個(gè)單元格,則偏移量就是其周邊8個(gè)單元格的坐標(biāo):

同時(shí),偏移后,我們還要判斷這個(gè)格子是否超出雷場(chǎng)。即便宜后x、y值不能小于0,且不能大于行或列的最大值。

偏移量offset和判斷是否超出雷場(chǎng)區(qū)域的方法如下:

// 【3-1-1】周圍8個(gè)坐標(biāo)相對(duì)于中心坐標(biāo)(0,0)的偏移量
var offset = [{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},
{x:-1,y:0},{x:1,y:0},
{x:-1,y:1},{x:0,y:1},{x:1,y:1},
];
// 【3-1-2】判斷坐標(biāo)為x,y的單元格cell是否超出了區(qū)域
MineField.prototype.outOfFiled = function(x, y){
return x<0 || x>=this.rowNum || y<0 || y>=this.colNum;
};

如果地雷周圍的單元格是數(shù)字,則我們不需要計(jì)算數(shù)值,應(yīng)排除。

// 【3-1】拿到cell周圍的所有的非雷單元格(應(yīng)該是數(shù)字的單元格)
MineField.prototype.getNumberCellsAround = function(cell){
let result = [];
for(let i=0,len=offset.length; i<len; i++){
// 【3-1-1】得到相對(duì)于cell偏移后的x、y坐標(biāo)
let x = cell.x + offset[i].x;
let y = cell.y + offset[i].y
// 【3-1-2】判斷坐標(biāo)為x,y的單元格cell是否超出了區(qū)域
if(this.outOfFiled(x, y)){
continue;
}
// 如果是地雷繼續(xù)下一次循環(huán)
let index = this.getIndexByXY(x,y); //x*this.colNum + y;
let cellSide = this.cells[index];
if (cellSide.info?=== "*"){
continue;
}
// 如果沒(méi)有超出雷場(chǎng)區(qū)域,且為非雷單元格,則添加到數(shù)組中
result.push(new Cell(x, y, ""));
}
return result;
};

【3-2】更新所有地雷周圍所有非雷(數(shù)字)單元格的數(shù)字標(biāo)記

// 【3-2】更新地雷周圍所有非雷(數(shù)字)單元格的數(shù)字標(biāo)記
MineField.prototype.updateNumberMarks = function(numberCells){
/**
* 設(shè)置邏輯:①如果原來(lái)info屬性為*,不需要設(shè)置,【已經(jīng)排除了】
* ②如果原來(lái)info屬性為"",證明是第一次標(biāo)記,標(biāo)記info為1
* ③如果原來(lái)屬性不為*,也不為空,則在原有值基礎(chǔ)上加1
*/
for(let i=0,len=numberCells.length; i<len; i++){
let index = this.getIndexByCell(numberCells[i]);
if(this.cells[index].info === ""){
this.cells[index].info = 1;
}else{
let num = parseInt(this.cells[index].info);
this.cells[index].info = ++num;
}
}
};

三、?ArrayUtils.js數(shù)組工具類(直接使用)

// 數(shù)組工具類
var ArrayUtils = function(){};

// 【1】初始化得到有序元素?cái)?shù)組:從[start,end)的自然數(shù)序列
ArrayUtils.initOrderArray = function(start, end){
let sortArray = [];
for(let i= start; i<end; i++){
sortArray.push(i);
}
return sortArray;
};

// 【2】從數(shù)組arr中隨機(jī)抽取count個(gè)元素,返回?cái)?shù)組
ArrayUtils.randChoiseFromArr = function(arr, count){
let result = arr;
// 隨機(jī)排序,打亂順序
result.sort(function(){
return 0.5 - Math.random();
});
// 返回打亂順序后的數(shù)組中的前count個(gè)元素
return result.slice(0,count);
};

// 【3】從從start到end中的連續(xù)整數(shù)中隨機(jī)抽取count個(gè)數(shù)字
ArrayUtils.randChoiseFromTo = function(start, end, count){
let arr = this.initOrderArray(start,end);
return this.randChoiseFromArr(arr, count);
};
// 【2】-【方式二】從數(shù)組arr中隨機(jī)抽取count個(gè)元素,返回?cái)?shù)組
ArrayUtils.getRandomArrayElements = function(arr, count) {
// 從0位置取到結(jié)束位置存入shffled數(shù)組
let shuffled = arr.slice(0);
let i = arr.length;
let min = i - count;
let temp = 0;
let index = 0;
// 隨機(jī)一個(gè)位置的元素和最后一個(gè)元素交換
// 隨機(jī)一個(gè)位置元素和倒數(shù)第二個(gè)元素交換
// 假設(shè)i=8,count=3,則min=5,
// 循環(huán)體中[i]=7,6,5,也就是說(shuō)最后三個(gè)元素要從數(shù)組中隨機(jī)取
// 循環(huán)結(jié)束后,從min=5的位置取到結(jié)束,即取3個(gè)元素。
while(i-- > min) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(min);
};

module.exports = ArrayUtils;

四、?數(shù)據(jù)校驗(yàn)測(cè)試

1. Game_mgr.js掛載到Canvas節(jié)點(diǎn)上

var MineField = require("MineField");

cc.Class({
extends: cc.Component,
properties: {
row : 9,
col : 9,
mineNum : 10,
},
onLoad () {
// 橫豎9個(gè)單元格,共10顆雷
this.mineField =?new MineField(this.row, this.col, this.mineNum);
console.log(this.mineField);
},
});

掛載到Canvas節(jié)點(diǎn)上,運(yùn)行測(cè)試結(jié)果如下:


3. 優(yōu)化測(cè)試-驗(yàn)證數(shù)據(jù)正確與否

發(fā)現(xiàn)顯示結(jié)果不便于核實(shí)數(shù)據(jù)是否正確,我們優(yōu)化下,在MineField中添加printResult方法:

// 【4】提供打印測(cè)試的方法,便于觀察數(shù)據(jù)是否正確
MineField.prototype.printResult = function(){
for(let i=0; i<this.rowNum; i++){
let line = "| ";
for(let j=0; j<this.colNum; j++){
let cell = this.cells[i*this.colNum + j];
line = line.concat(cell.info?+ " | ");
}
console.log(line);
}
};

為了打印時(shí)能夠上下對(duì)齊,我們將MineField.js代碼中原有""(空字符串)替換成" "(空格)。

然后,將Game_mgr.js中的代碼做如下調(diào)整:

//console.log(this.mineField);
this.mineField.printResult();

運(yùn)行,瀏覽器console窗口如下:正確!

五、?數(shù)據(jù)與視圖綁定

新建一個(gè)空節(jié)點(diǎn)MineField作為雷場(chǎng),將res中的block拖到MineField內(nèi),作為地磚,在block節(jié)點(diǎn)內(nèi)新建空節(jié)點(diǎn)around_bombs,在此節(jié)點(diǎn)上添加Label組件,用于顯示此地磚的信息info。之后將block做成預(yù)制體,便于動(dòng)態(tài)生成雷場(chǎng)所有地磚。

動(dòng)態(tài)生成的過(guò)程中,將每個(gè)地磚跟MineField的cells數(shù)組中的元素綁定。

在Game_mgr.js的properties中添加屬性,同時(shí)通過(guò)編輯器綁定屬性值:

// 地磚預(yù)制體、和根節(jié)點(diǎn)
block_prefab : {type:cc.Prefab, default:null,},
block_root : {type:cc.Node, default:null,},

在Game_mgr.js的onLoad方法中添加如下代碼:

// 初始化游戲界面
this.showMineField();

在Game_mgr.js中增加showMineField實(shí)現(xiàn):

// 顯示雷場(chǎng)格子
showMineField(){
// 獲取地磚預(yù)制體的寬度
var block_width = this.block_prefab.data.width;
// 計(jì)算第一個(gè)格子相對(duì)于中心錨點(diǎn)的偏移量
var x_offset = - block_width * this.col/2;
var y_offset = block_width * this.row/2;
// block的錨點(diǎn)也在中心,而不是左下角,故初始偏移量要往右上角移動(dòng)
x_offset += block_width/2;
y_offset += block_width*2; // 稍微往上移點(diǎn)
for(var i=0; i<this.row; i++){
for(var j=0; j<this.col; j++){
var block = cc.instantiate(this.block_prefab);
// 【*】將每個(gè)地磚跟MineField的cells數(shù)組中的元素綁定
var index = this.mineField.getIndexByXY(i,j);
block.cell = this.mineField.cells[index];
this.block_root.addChild(block);
// 注意:i是行,j是列,當(dāng)然行列數(shù)相等是不會(huì)有影響,
// 【*】行列不等時(shí)會(huì)影響后續(xù)邊界判斷邏輯
block.setPosition(j*block_width+x_offset, y_offset-i*block_width);
console.log("block[",i,j,"]=",block.cell.toString());
}
}
},

在Cell.js中增加toString方法顯示對(duì)象信息:

Cell.prototype.toString = function () {
return "{ x : " + this.x + ", y : " + this.y + ", info : " +?this.info?+ " }";
}

編譯運(yùn)行,結(jié)果如下:

將信息顯示到地磚上:

block.cell = this.mineField.cells[index];
// 顯示地磚內(nèi)部信息
this.showBlockInnerInfo(block);

信息顯示到地磚上的實(shí)現(xiàn)方法(便于后續(xù)觸摸調(diào)用):

// 顯示地磚內(nèi)部信息
showBlockInnerInfo(block){
block.getChildByName("around_bombs").getComponent(cc.Label).string = block.cell.info;
},

編譯運(yùn)行結(jié)果如下:

仔細(xì)思考,發(fā)現(xiàn)剛才Game_mgr.js其實(shí)就是控制MineField這個(gè)節(jié)點(diǎn)的,故我們將其修改為MineField_Ctrl.js。將Canvas上的用戶自定義組件remove,在MineField節(jié)點(diǎn)上添加MineField_Ctrl組件,將其中block_root屬性去掉,將代碼中this.block_root替換為this.node。


Cocos掃雷游戲核心算法思想的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
电白县| 盘山县| 张北县| 马鞍山市| 建湖县| 托克托县| 唐海县| 山东| 山丹县| 孙吴县| 辽阳市| 黄山市| 芮城县| 繁昌县| 金坛市| 五华县| 乐清市| 扎囊县| 罗平县| 汉川市| 怀化市| 平顺县| 佛坪县| 红桥区| 仪陇县| 怀远县| 惠东县| 鹤岗市| 巢湖市| 临沂市| 淅川县| 阿克| 历史| 新竹市| 沛县| 澎湖县| 凌源市| 休宁县| 焦作市| 皋兰县| 德令哈市|