openal是什么
引言:扫雷是系统自带的经典小游戏,以前上学那会上机的时候就经常玩这个,趁着五一假期的最后一天,用canvas编写了这个小游戏,看着还行,不知道会不会有什么我没发现的bug,难度目前是设置成简易的,如果要改难度,代码稍做修改即可。
效果图
实现思路1.创建9行9列的二维数组。
2.设置雷:随机行数 i 和列数 j,根据随机到 i、j 从二维数组中取出对应的元素,将取到的元素设置一个属性type等于1,表示当前元素已经是雷,并且递增雷计数器,然后递归调用;如果取到的元素已经是雷了,则跳过继续执行,雷计数器达到设定的最大值就跳出递归。3.计算每个元素周围的雷数量(周围指的是 左上、上、右上、右、右下、下、左下、左 这8个位置),当前位置显示对应的数字(待会内容里面细说)
4.同样根据这个二维数组来创建遮罩的小方块,正好盖住之前创建的图形。
5.点击这个遮罩的小方块则触发揭开,揭开后根据对应的数字或者雷做不同的操作。
代码实现创建背景及相关元素 //绘制背景及相关默认元素 Saolei.prototype.drawBG=function(){ var image,img,sx=0,sy=0,sWidth=141,sHeight=54,dx=20,dy=340,dWidth=141,dHeight=54; //计时 image = this.imgObj[‘common’][15]; img = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight}); this.renderArr.push(img); sx=0,sy=0,sWidth=141,sHeight=52,dx=180,dy=340,dWidth=141,dHeight=52; //计雷 image = this.imgObj[‘common’][14]; img = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight}); this.renderArr.push(img); //创建一个方形区域 var rect = new _.Rect({ x:24, y:44, width:289, height:289, stroke:true }) this.renderArr.push(rect); sx=0,sy=0,sWidth=100,sHeight=40,dx=120,dy=2,dWidth=100,dHeight=40; //重新开始按钮 image = this.imgObj[‘common’][21]; img = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight}); this.renderArr.push(img); this.reStartObj=img; }
创建雷和显示对应的图片1.随机row 和 col,并从二维数组中获取到这个对象;
2.判断他是否是雷,如果是则跳过当前;
3.如果当前不是雷,则标记当前对象为雷对象,并且更改图片;
4.递归,当达到设定的数量时跳出。
//创建被遮盖 Saolei.prototype.createUnder=function(){ var image,img,sx=0,sy=0,sWidth=79,sHeight=79,dx=0,dy=0,dWidth=32,dHeight=32; var rows = this.rows;//行 var cols = this.cols;//列 image = this.imgObj[‘common’][9]; //二维网格数组 var gridArr=[]; var arr = this.gridArr,cell; for(var i=0;i<rows;i++){//行 dy = 45+i*dHeight; gridArr[i]=[]; for(var j=0;j<cols;j++){//列 dx = 25+j*dWidth; img = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight}); img.type=0; this.renderArr.push(img); gridArr[i][j]=img; } } this.gridArr=gridArr; //创建雷 this.createLei(); } //创建雷 Saolei.prototype.createLei=function(){ //当达到设定的数量时跳出 if(this.leiMaxCount<=0) { return ; } var arr = this.gridArr; /* 1.随机row 和 col,并从二维数组中获取到这个对象 2.判断他是否是雷,如果是则跳过当前 3.如果当前不是雷,则标记当前对象为雷对象,并且更改图片 4.递归,当达到设定的数量时跳出 */ var row = _.getRandom(0,this.rows); var col = _.getRandom(0,this.cols); var cell = arr[row][col]; if(cell.type==0){ //标记为雷 cell.type=1; cell.image = this.imgObj[‘common’][18]; this.leiMaxCount–; console.log(row,col); } //递归 this.createLei(); }
计算周围雷的数量并显示1.循环之前定义的二维数组
2.如果当前元素的下标是(i,j),则左上为(i-1,j-1),上为(i-1,j ),右上为(i-1,j+1),以此类推,如下图所示:
3.分别取出这些元素,并判断他们是不是雷,如果是则计数累加,最后将计数对应到相应的图片,然后显示出来。
//计算周边雷的数量并更改对象的相关参数 Saolei.prototype.computedLei=function(){ var arr = this.gridArr,cell; for(var i=0;i<arr.length;i++){//行 for(var j=0;j<arr[i].length;j++){//列 cell = arr[i][j]; if(cell.type==1){//当前是雷则直接跳过 continue; } var count=0; //左上 var ci = i-1,cj = j-1,ccell; if(ci>=0 && cj>=0){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //上 ci = i-1,cj = j,ccell; if(ci>=0 && cj>=0){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //右上 ci = i-1,cj = j+1,ccell; if(ci>=0 && cj<this.cols){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //右 ci = i,cj = j+1,ccell; if(cj<this.cols){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //右下 ci = i+1,cj = j+1,ccell; if(ci<this.rows && cj<this.cols){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //下 ci = i+1,cj = j,ccell; if(ci<this.rows){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //左下 ci = i+1,cj = j-1,ccell; if(ci<this.rows && cj >=0){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //左 ci = i,cj = j-1,ccell; if(cj >= 0){ ccell = arr[ci][cj]; if(ccell.type==1){ count++; } } //设定周围雷的数量 cell.count=count; if(count==0){//因为0那张图片下标用的9 count=9; } //更换图片 cell.image = this.imgObj[‘common’][count]; } } }
创建遮罩 //创建遮盖 Saolei.prototype.createOver=function(){ var image,img,sx=0,sy=0,sWidth=79,sHeight=79,dx=0,dy=0,dWidth=32,dHeight=32; image = this.imgObj[‘common’][10]; var arr = this.gridArr; for(var i=0;i<arr.length;i++){//行 this.overArr[i]=[]; for(var j=0;j<arr[i].length;j++){//列 dy = 45+i*dHeight; dx = 25+j*dWidth; img = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight}); img.i=i,img.j=j; this.renderArr.push(img); this.overArr[i][j]=img; } } }
创建计时和计数器//创建计数和计时器 Saolei.prototype.createCount=function(){ //计时器 var x=115,y=382,content=0; var text = new _.Text({ x:x, y:y, text:content, font:’26px ans-serif’, textAlign:’center’, fill:true, fillStyle:’white’ }); this.renderArr.push(text); this.timeCountObj=text; x=222,y=382,content=this.leiCount; var text = new _.Text({ x:x, y:y, text:content, font:’26px ans-serif’, textAlign:’center’, fill:true, fillStyle:’white’ }); this.renderArr.push(text); this.leiCountObj=text; }
加入鼠标移动事件//鼠标移动事件 Saolei.prototype.mouseMove=function(e){ if(this.endAnimate)return ; var pos = _.getOffset(e);//获取鼠标位置 var isCatch=false; if(this.reStartObj.isPoint(pos)){ this.el.style.cursor = ‘pointer’;//改为手状形态 }else{ this.el.style.cursor = ”;//改为普通形态 } if(this.end)return ;//结束了已经 if(!isCatch){ //循环遮罩数组 var arr = this.overArr,cell; for(var i=0;i<arr.length;i++){//行 for(var j=0;j<arr[i].length;j++){//列 cell = arr[i][j]; if(cell.isPoint(pos)&& !cell.open){//鼠标捕捉,被打开的同样不捕获 if(!cell.state){//打上标记的不做处理 cell.image= this.imgObj[‘common’][11]; } }else{ if(!cell.state){//打上标记的不做处理 cell.image= this.imgObj[‘common’][10]; } } } } this.render(); } }加入鼠标点击事件//鼠标点击事件 Saolei.prototype.mouseClick=function(e){ if(this.endAnimate)return ;//结束动画的时候不许点击 var pos = _.getOffset(e);//获取鼠标位置 if(this.reStartObj.isPoint(pos)){//重新开始被点击 this.restart(); return ; } if(this.end)return ;//结束了已经 //循环遮罩数组 var arr = this.overArr,cell,cellArr=this.gridArr; for(var i=0;i<arr.length;i++){//行 for(var j=0;j<arr[i].length;j++){//列 cell = arr[i][j]; if(cell.isPoint(pos) && !cell.open){//鼠标捕捉,被打开的同样不捕获 if(!this.start){ doStart.call(this); } //移出当前对象 cell.open=true;//被打开 this.clearAssign(this.renderArr,cell); //获取对应的格子对象 var item = cellArr[i][j]; if(item.type==1){//如果是雷,则显示爆炸动画并提示失败 this.boom(item); }else{//如不是雷 if(item.count==0){//判断周围雷数量,如果是0则打开周围,并依次递归 this.openOver(cell.i,cell.j); } //判断是否达到胜利的条件 this.successOrNot(); } } } } this.render(); function doStart(){ this.start=true; this.timmer = setInterval(function(){ this.timmerCount++; this.timeCountObj.text=this.timmerCount; this.render(); }.bind(this),1000); } }成功判定1未打开的数量与雷的数量相同
//判断是否成功 Saolei.prototype.successOrNot=function(cell){ var arr = this.overArr,cell,count=0; for(var i=0;i<arr.length;i++){//行 for(var j=0;j<arr[i].length;j++){//列 cell = arr[i][j]; if(!cell.open){ count++; } } } if(count==this.leiSucCount){//未打开的数量和雷的数量一样,表示成功 this.end=true; clearInterval(this.timmer);//清除计时器的定时任务 //打开所有的雷 this.openAllLei(); //显示成功表情(延时) setTimeout(this.endShow.bind(this,’suc’),100); } }成功判定2标记为雷(插旗)的数量与类总数相同
//判断是否成功 -根据插红旗 Saolei.prototype.successOrNot2=function(cell){ var arr = this.overArr,cell,count=0,gridArr = this.gridArr,item; var count=0; for(var i=0;i<arr.length;i++){//行 for(var j=0;j<arr[i].length;j++){//列 cell = arr[i][j]; if(cell.state==1){//红旗 item=gridArr[i][j]; if(item.type==1){//正好又是雷 count++; } } } } if(count==this.leiSucCount){//未打开的数量和雷的数量一样,表示成功 this.end=true; clearInterval(this.timmer);//清除计时器的定时任务 //打开所有的雷 this.openAllLei(); //显示成功表情(延时) setTimeout(this.endShow.bind(this,’suc’),100); } }触雷效果鼠标点击雷后,会触发雷爆炸的一个动画,这是通过图片的切换来实现的
//爆炸效果 Saolei.prototype.boom=function(cell){ this.end=true; this.endAnimate=true; clearInterval(this.timmer);//清除计时器的定时任务 //开启爆炸的动画 this.timmer = setInterval(this.boomAnimate.bind(this,cell),100) } //爆炸动画 Saolei.prototype.boomAnimate=function(cell){ //切换图片 cell.index = (cell.index || 0)+1; if(cell.index>this.boomCount){ //结束动画 clearInterval(this.timmer); //因为图片有些不一样,需要修正一下 cell.sWidth=79; cell.sHeight=79; cell.image= this.imgObj[‘common’][18]; //打开所有的雷 this.openAllLei(); //显示失败表情(延时) setTimeout(this.endShow.bind(this),100); this.endAnimate=false; this.render(); return ; } //因为图片有些不一样,需要修正一下 cell.sWidth=61; cell.sHeight=53; cell.image= this.imgObj[‘boom’][cell.index]; this.render(); }加入鼠标右键事件此事件是做插旗或者标记为未知等操作的。
//右键事件 Saolei.prototype.contextMenu=function(e){ if(this.end)return ;//结束了已经 var e = e||window.event; //取消右键默认事件 e.preventDefault && e.preventDefault(); var pos = _.getOffset(e);//获取鼠标位置 //循环遮罩数组 var arr = this.overArr,cell,cellArr=this.gridArr; for(var i=0;i<arr.length;i++){//行 for(var j=0;j<arr[i].length;j++){//列 cell = arr[i][j]; if(cell.isPoint(pos) && !cell.open){//鼠标捕捉,被打开的同样不捕获 //右键切换 if(!cell.state){//如果是没有状态的,则标记为雷,小旗 cell.state=1; cell.image= this.imgObj[‘common’][12]; this.leiCount–; this.leiCountObj.text=this.leiCount; //判断如果小旗数量和数据都对上了,也判断为成功 this.successOrNot2(cell); }else if(cell.state==1){//如果状态为雷的,标记为未知,问号 cell.state=2; cell.image= this.imgObj[‘common’][13]; this.leiCount++; this.leiCountObj.text=this.leiCount; }else if(cell.state==2){//如果状态为未知的,则现在原来的 cell.state=0; cell.image= this.imgObj[‘common’][10]; } } } } this.render(); }最后加入重新开始事件,胜利和失败图片显示就完成了。欢迎各位大佬 点赞+关注,谢谢!关注下方公众号,回复 【扫雷】 下载源码