还记得小时上微机课,老师什么都不会,讲了一会怎么开机关机后就让我们随便玩了,但是连网都没有啊,只能玩windows自带的游戏,最经典的当然要数扫雷了,先来回味一下最初版本的扫雷
后来windows更新换代,扫雷也换了一下皮肤,虽然大多数人可能再也没点开过扫雷那个游戏
现在我们就来山寨一个经典版的扫雷,你会发现看着简单的扫雷好像做起来也要动一下脑子
首先我们新建一个工程
作为新时代的接班人,有必要跟国际接一下轨
如果我们起个名字叫“saolei”是不是很low?
所以我们应该把工程的名字叫做“Minesweeper”
建立一些文件夹,导入资源文件什么乱七八糟的。。。
(那些很基础的东西我就不讲了,这篇主要侧重实现扫雷的思路)
这个Tile还是有很多种形态的,就像这些
我们知道Tile是可以点击的,点开前我们可以对他进行标记(插旗,问号),点开后他会显示周围雷的情况(1到8或者空或者是雷),我们为Tile添加两个用于区别的属性,一个是state,一个是type,state代表Tile的点击状态,包括:None(未点击),Flag(插旗),Doubt(疑问),Cliked(点开),type代表Tile点开后的种类,包括数字和雷,之所以要用两个属性区别,是因为我们要对每一个Tile进行初始化,每一个Tile在开始游戏时就要确定下来。
Tile.js
const TYPE = cc.Enum({
ZERO:0,
ONE:1,
TWO:2,
THREE:3,
FOUR:4,
FIVE:5,
SIX:6,
SEVEN:7,
EIGHT:8,
BOMB:9
});
const STATE = cc.Enum({
NONE:-1,//未点击
CLIKED:-1,//已点开
FLAG:-1,//插旗
DOUBT:-1,//疑问
});
//其他脚本访问
module.exports = {
STATE:STATE,
TYPE:TYPE,
};
cc.Class({
extends: cc.Component,
properties: {
picNone:cc.SpriteFrame,
picFlag:cc.SpriteFrame,
picDoubt:cc.SpriteFrame,
picZero:cc.SpriteFrame,
picOne:cc.SpriteFrame,
picTwo:cc.SpriteFrame,
picThree:cc.SpriteFrame,
picFour:cc.SpriteFrame,
picFive:cc.SpriteFrame,
picSix:cc.SpriteFrame,
picSeven:cc.SpriteFrame,
picEight:cc.SpriteFrame,
picBomb:cc.SpriteFrame,
_state: {
default: STATE.NONE,
type: STATE,
visible: false
},
state: {
get: function () {
return this._state;
},
set: function(value){
if (value !== this._state) {
this._state = value;
switch(this._state) {
case STATE.NONE:
this.getComponent(cc.Sprite).spriteFrame = this.picNone;
break;
case STATE.CLIKED:
this.showType();
break;
case STATE.FLAG:
this.getComponent(cc.Sprite).spriteFrame = this.picFlag;
break;
case STATE.DOUBT:
this.getComponent(cc.Sprite).spriteFrame = this.picDoubt;
break;
default:break;
}
}
},
type:STATE,
},
type: {
default:TYPE.ZERO,
type:TYPE,
},
},
showType:function(){
switch(this.type){
case TYPE.ZERO:
this.getComponent(cc.Sprite).spriteFrame = this.picZero;
break;
case TYPE.ONE:
this.getComponent(cc.Sprite).spriteFrame = this.picOne;
break;
case TYPE.TWO:
this.getComponent(cc.Sprite).spriteFrame = this.picTwo;
break;
case TYPE.THREE:
this.getComponent(cc.Sprite).spriteFrame = this.picThree;
break;
case TYPE.FOUR:
this.getComponent(cc.Sprite).spriteFrame = this.picFour;
break;
case TYPE.FIVE:
this.getComponent(cc.Sprite).spriteFrame = this.picFive;
break;
case TYPE.SIX:
this.getComponent(cc.Sprite).spriteFrame = this.picSix;
break;
case TYPE.SEVEN:
this.getComponent(cc.Sprite).spriteFrame = this.picSeven;
break;
case TYPE.EIGHT:
this.getComponent(cc.Sprite).spriteFrame = this.picEight;
break;
case TYPE.BOMB:
this.getComponent(cc.Sprite).spriteFrame = this.picBomb;
break;
default:break;
}
}
});
Game.js
const GAME_STATE = cc.Enum({
PREPARE:-1,
PLAY:-1,
DEAD:-1,
WIN:-1
});
const TOUCH_STATE = cc.Enum({
BLANK:-1,
FLAG:-1,
});
cc.Class({
extends: cc.Component,
properties: {
tilesLayout:cc.Node,
tile:cc.Prefab,
btnShow:cc.Node,
tiles:[],//用一个数组保存所有tile的引用,数组下标就是相应tile的tag
picPrepare:cc.SpriteFrame,
picPlay:cc.SpriteFrame,
picDead:cc.SpriteFrame,
picWin:cc.SpriteFrame,
gameState:{
default:GAME_STATE.PREPARE,
type:GAME_STATE,
},
touchState:{//左键点开tile,右键插旗
default:TOUCH_STATE.BLANK,
type:TOUCH_STATE,
},
row:0,
col:0,
bombNum:0,
},
onLoad: function () {
this.Tile = require("Tile");
var self = this;
for(let y=0;y
for(let x=0;x
let tile = cc.instantiate(this.tile);
tile.tag = y*this.col+x;
tile.on(cc.Node.EventType.MOUSE_UP,function(event){
if(event.getButton() === cc.Event.EventMouse.BUTTON_LEFT){
self.touchState = TOUCH_STATE.BLANK;
}else if(event.getButton() === cc.Event.EventMouse.BUTTON_RIGHT){
self.touchState = TOUCH_STATE.FLAG;
}
self.onTouchTile(this);
});
this.tilesLayout.addChild(tile);
this.tiles.push(tile);
}
}
this.newGame();
},
newGame:function(){
//初始化场景
for(let n=0;n
this.tiles[n].getComponent("Tile").type = this.Tile.TYPE.ZERO;
this.tiles[n].getComponent("Tile").state = this.Tile.STATE.NONE;
}
//添加雷
var tilesIndex = [];
for(var i=0;i
tilesIndex[i] = i;
}
for(var j=0;j
var n = Math.floor(Math.random()*tilesIndex.length);
this.tiles[tilesIndex[n]].getComponent("Tile").type = this.Tile.TYPE.BOMB;
tilesIndex.splice(n,1);//从第n个位置删除一个元素
//如果没有splice方法可以用这种方式
// tilesIndex[n] = tilesIndex[tilesIndex.length-1];
// tilesIndex.length--;
}
//标记雷周围的方块
for(var k=0;k
var tempBomb = 0;
if(this.tiles[k].getComponent("Tile").type == this.Tile.TYPE.ZERO){
var roundTiles = this.tileRound(k);
for(var m=0;m
if(roundTiles[m].getComponent("Tile").type == this.Tile.TYPE.BOMB){
tempBomb++;
}
}
this.tiles[k].getComponent("Tile").type = tempBomb;
}
}
this.gameState = GAME_STATE.PLAY;
this.btnShow.getComponent(cc.Sprite).spriteFrame = this.picPlay;
},
//返回tag为i的tile的周围tile数组
tileRound:function(i){
var roundTiles = [];
if(i%this.col > 0){//left
roundTiles.push(this.tiles[i-1]);
}
if(i%this.col > 0 && Math.floor(i/this.col) > 0){//left bottom
roundTiles.push(this.tiles[i-this.col-1]);
}
if(i%this.col > 0 && Math.floor(i/this.col) < this.row-1){//left top
roundTiles.push(this.tiles[i+this.col-1]);
}
if(Math.floor(i/this.col) > 0){//bottom
roundTiles.push(this.tiles[i-this.col]);
}
if(Math.floor(i/this.col) < this.row-1){//top
roundTiles.push(this.tiles[i+this.col]);
}
if(i%this.col < this.col-1){//right
roundTiles.push(this.tiles[i+1]);
}
if(i%this.col < this.col-1 && Math.floor(i/this.col) > 0){//rihgt bottom
roundTiles.push(this.tiles[i-this.col+1]);
}
if(i%this.col < this.col-1 && Math.floor(i/this.col) < this.row-1){//right top
roundTiles.push(this.tiles[i+this.col+1]);
}
return roundTiles;
},
onTouchTile:function(touchTile){
if(this.gameState != GAME_STATE.PLAY){
return;
}
switch(this.touchState){
case TOUCH_STATE.BLANK:
if(touchTile.getComponent("Tile").type === 9){
touchTile.getComponent("Tile").state = this.Tile.STATE.CLIKED;
this.gameOver();
return;
}
var testTiles = [];
if(touchTile.getComponent("Tile").state === this.Tile.STATE.NONE){
testTiles.push(touchTile);
while(testTiles.length){
var testTile = testTiles.pop();
if(testTile.getComponent("Tile").type === 0){
testTile.getComponent("Tile").state = this.Tile.STATE.CLIKED;
var roundTiles = this.tileRound(testTile.tag);
for(var i=0;i
if(roundTiles[i].getComponent("Tile").state == this.Tile.STATE.NONE){
testTiles.push(roundTiles[i]);
}
}
}else if(testTile.getComponent("Tile").type > 0 && testTile.getComponent("Tile").type < 9){
testTile.getComponent("Tile").state = this.Tile.STATE.CLIKED;
}
}
this.judgeWin();
}
break;
case TOUCH_STATE.FLAG:
if(touchTile.getComponent("Tile").state == this.Tile.STATE.NONE){
touchTile.getComponent("Tile").state = this.Tile.STATE.FLAG;
}else if(touchTile.getComponent("Tile").state == this.Tile.STATE.FLAG){
touchTile.getComponent("Tile").state = this.Tile.STATE.NONE;
}
break;
default:break;
}
},
judgeWin:function(){
var confNum = 0;
//判断是否胜利
for(let i=0;i
if(this.tiles[i].getComponent("Tile").state === this.Tile.STATE.CLIKED){
confNum++;
}
}
if(confNum === this.tiles.length-this.bombNum){
this.gameState = GAME_STATE.WIN;
this.btnShow.getComponent(cc.Sprite).spriteFrame = this.picWin;
}
},
gameOver:function(){
this.gameState = GAME_STATE.DEAD;
this.btnShow.getComponent(cc.Sprite).spriteFrame = this.picDead;
},
onBtnShow:function(){
if(this.gameState === GAME_STATE.PREPARE){
this.newGame();
}
if(this.gameState === GAME_STATE.DEAD){
// this.bombNum--;
this.newGame();
}
if(this.gameState === GAME_STATE.WIN){
// this.bombNum++;
this.newGame();
}
}
});
var tilesIndex = [];
for(var i=0;i<this.tiles.length;i++){
tilesIndex[i] = i;
}
for(var j=0;j<this.bombNum;j++){
var n = Math.floor(Math.random()*tilesIndex.length);
this.tiles[tilesIndex[n]].getComponent("Tile").type = this.Tile.TYPE.BOMB;
tilesIndex.splice(n,1);//从第n个位置删除一个元素
//如果没有splice方法可以用这种方式
// tilesIndex[n] = tilesIndex[tilesIndex.length-1];
// tilesIndex.length--;
}
计算Tile周围雷的数目
先要建立一个能得到Tile周围Tile数组的方法(Game.js里的tileRound方法),要注意边界检测,然后对返回的Tile数组判断Type就行了
点开相连空地区域
最简单的方法是用递归,但是调用函数的次数太多了,然后Creator就出Bug了,所以这里我们用一种非递归的方式实现
简单的流程图示意:
从点开的这个Tile进行处理,调用tileRound方法判断周围Tile是否是空地且未被点开,如果不是,则跳过,如果是,则将其自动点开,同时把这几个位置加入栈循环判断。
流程图如下:
当前位置是空白位置?----否---> 非空白的处理
|
| 是
|
V
入栈
|
V
+--->栈为空?-------->是---> 结束
| |
| |否
| |
| V
| 出栈一个元素
| |
| V
| 点开该元素所指的位置
| |
| V
| 上左下右的位置如果是空白且未点开则入栈
| |
--------+
来一句算法的名言:所有递归都能转化为循环(以后你跟别人聊天时,要不经意的说出这句话,逼格瞬间提升)
就这样吧,刚考完科目一整个人有点飘,写的可能有点乱,有什么不懂的可以留言
资源和工程文件: http://download.csdn.net/detail/potato47/9634246
-
微信号:xinshouit (新手程序员) 更新会在里面通知