上周末突然想尝试着做一个扫雷游戏。扫雷游戏我是很喜欢玩的,更年轻时候,晚上晚上几点钟的时候都有。。。
虽然之前没有写过HTML5游戏,不过我感觉肯定能写出来的。虽然对于写H5游戏有哪些步骤什么的都不是很明了。
另外一点,写这个游戏,所有的算法都必须是自己思考出来,不能在写之前去参考别人的思路。
因为一旦参考了,就会受到别人思路左右,永远没有机会从无到有地去思考这个自己喜欢过的游戏了。
然后上周末完成了基本雏形,上周闲的时候又断断续续地完善。一个功能一个功能地实现。 中间还抽空做了一个贪吃蛇。 相比扫雷而言,贪吃蛇的逻辑就相当简单了。
先看几个过程中的版本:
完成版本,已经像模像样了呢!用了网上下的图片扫雷资源,数字,旗子之类的。
实现的功能,按完成步骤。
1.根据格子数量,初始化canvas画布。绘制格子。 初始化所需要的数组。包含格子信息的格子数组。
2.鼠标划过每一个格子改变格子。 需要判断鼠标在画布上的坐标。然后根据画布坐标判断格子。
3.随机生成雷。 do while循环,生成不重复的格子坐标保存到地雷数组和格子数组。
4.点击打开雷。显示周围雷的数量。如果雷的数量为0。则用一个递归计算这个雷周围8个格子里有没有周边雷数量为0的格子,如果有,再计算这个格子周围。。。直到把相连的所有周边雷数量为0的格子找出来,放入一个数组。然后再把这个数组里所有格子以及它们周围的8个格子显示出来,显示每个格子周围雷的数量 。雷的数量用不同颜色。
不过我感觉这个算法效率还不是很高。要优化。
5.被打开过的格子或被标记过的格子,没有hover事件。
6.标记格子。用旗子或问号。 屏蔽网页原本的右键事件。
7.加上时间,以及雷的数量的标记。以及点击的时候才生成雷。避免第一次点击 就点到雷。
8.双键同击事件。如果周边雷的数量等于周边标记的数量,就把周边没有打开没有标记的点开。不等于就动态展现周边剩下哪几个没打开的。
9.点中的雷以记错误的标记用不同的图像表达出来。
10.用网上下的资源美化。增加级别选择。高级,中级,初级,以及自定义。优化界面效果。
部分代码:
var Mine = function (ele,panewidth,paneheight,minenum,tagele,timeele) {
this.PANE_SIZE = 16;//每个格子的像素px大小。
this.paneheight = paneheight;//有几行
this.panewidth = panewidth;//有几列
this.minenum = minenum;//有几个雷
this.ele = document.getElementById(ele);
this.cxt = this.ele.getContext("2d");
this.tagele = tagele;
this.timeele = timeele;
}
init:function(){
//画格子
this.ele.width = this.PANE_SIZE * this.panewidth;
this.ele.height = this.PANE_SIZE * this.paneheight;
this.oldPos = [0,0];//鼠标上一个停留的位置。默认值。用于处理hover事件。
this.cellArr=[];//格子信息保存数组。保存每个格子是不是雷,当前是否有标记。
this.mineArr=[];//地雷位置数组。
this.time = 0;//操作时间
this.notTaged = this.minenum;//未标记数量
this.numToImage(this.notTaged,this.tagele);//将标记数字转成图片
this.numToImage(this.time,this.timeele);//将时间数字转成图片
this.mousedownArr='';//鼠标点下事件,是为了双键同击事件。
this.createCells();//初始化cellArr数组,并涂上颜色。
this.inited = false;//是否初始化过。
clearInterval(this.timer);//时间跳动
//绑定事件
this.onmousemove();//鼠标在上面移动,触发每个格子的
this.onmouseout();//鼠标移出canvas的事件。
this.onmousedown();//鼠标点下事件,是为了双键同击事件。
this.onclick();//点击方格事件
this.preRightMenu();//阻止右键菜单。
}
createMines:function(pos){ //生成雷的位置。保存到一个数组[[2,3],[4,6]];
var minenum = this.minenum;
var mineArr = this.mineArr;
var mineItem='';
var cellArr = this.cellArr;
for (var i = 0; i < minenum; i++) {
//如果生成的重复了就重新生成。
do{
mineItem = [getRandom(this.panewidth),getRandom(this.paneheight)];
}while(in_array(mineItem,mineArr)||pos.toString()== mineItem.toString());
cellArr[mineItem[0]][mineItem[1]].isMine = true;
mineArr.push(mineItem);
};
}
getCellArea:function(pos){//根据格子坐标返回一个格子左上角的像素坐标[32,666];
return [(pos[0]-1)*this.PANE_SIZE+1,(pos[1]-1)*this.PANE_SIZE+1];
},
getCellPos:function(coordinate){//根据像素坐标返回格子坐标。[3,5];
return [Math.ceil(coordinate.x/this.PANE_SIZE),Math.ceil(coordinate.y/this.PANE_SIZE)];
}
//获取坐标:
function getEventPosition(ev){
var x, y;
if (ev.layerX || ev.layerX == 0) {
x = ev.layerX;
y = ev.layerY;
}else if (ev.offsetX || ev.offsetX == 0) { // Opera
x = ev.offsetX;
y = ev.offsetY;
}
return {x: x, y: y};
}
drawCell:function(pos,type){//绘制不同种类的格子。
var area = this.getCellArea(pos);
var cxt = this.cxt;
var image = new Image();
var src;
var srcArr = ["res/blank.bmp","res/0.bmp","res/flag.bmp","res/ask.bmp","res/mine.bmp","res/blood.bmp","res/error.bmp"];
//1正常格 2mouseover格子 3旗子格 4问号格 5正常雷格 6点中雷格 7.错误标记
var index = type -1;
image.src =srcArr[index];
image.onload = function(){
cxt.drawImage(image,area[0],area[1],16,16);
}
},
drawNum:function(pos,num){//绘制数字
var area = this.getCellArea(pos);
var cxt = this.cxt;
var image = new Image();
image.src = "res/"+num+".bmp";
image.onload = function(){
cxt.drawImage(image,area[0],area[1],16,16);
}
}
calZeroMine:function(pos,zeroArr){//使用递归求出周围所有的全为0的区域。算法效率还不是很高。
var cellArr = this.cellArr;
// var aroundArr = [[pos[0]-1,pos[1]],[pos[0],pos[1]-1],[pos[0],pos[1]+1], [pos[0]+1,pos[1]]];//只保留上下左右 好像还是会有一点问题。
var aroundArr = [[pos[0]-1,pos[1]-1],[pos[0]-1,pos[1]],[pos[0]-1,pos[1]+1],[pos[0],pos[1]-1],[pos[0],pos[1]+1],[pos[0]+1,pos[1]-1],[pos[0]+1,pos[1]],[pos[0]+1,pos[1]+1]];
var aroundMineNum = 0;
for (var i = 0; i < aroundArr.length; i++) {
aroundMineNum = this.calAround(aroundArr[i]);//附近雷的数量
if(aroundMineNum == 0 && this.checkCell(aroundArr[i]) && cellArr[aroundArr[i][0]][aroundArr[i][1]].isMine == false &&!in_array(aroundArr[i],zeroArr)){
zeroArr.push(aroundArr[i]);
this.calZeroMine(aroundArr[i],zeroArr);//调用自己,递归。
}
};
return zeroArr;
}
var mine1 = new Mine("mine1",30,16,99,"game-tag-images","game-time-images");
mine1.init();
演示地址:http://runningls.com/demos/2016/mine/mine.html
github:https://github.com/liusaint/games/tree/master/mine
欢迎留言讨论。
转载注明出处:http://blog.csdn.net/liusaint1992/article/details/50531186