文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何利用js+canvas实现扫雷游戏

2023-07-02 00:13

关注

这篇文章主要介绍“如何利用js+canvas实现扫雷游戏”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何利用js+canvas实现扫雷游戏”文章能帮助大家解决问题。

代码如下

<body>     胜利条件,找出所有地雷并标记    <form action="javaScript:createContent()">        <div id="message" >地雷数量必须小于地图大小xy的平方</div>        <br />         地图大小xy :<input id="xyNum" type="number" required="true" name="points" min="1" max="50"  />         booNum:<input id="booNum" type="number" required="true" name="points" min="1" max="2500"/>        <input type="submit" value="OK" : />        <br /> 1. 输入宽度 <br />2. 输入地雷数(地雷数小于宽*宽) <br /> 3. 单击确定  <br />        鼠标右键:<br />        第一次:标记您的猜测<br />        第二次: 取消标签<br />    </form>    <div id= 'game'>     </div>    <script src="./js/MarkObs.js"></script>    <script src="./js/Isboo.js"></script>    <script src="./js/lei.js"></script>    <script>    let xy = document.getElementById('xyNum');    let boo = document.getElementById('booNum');    let meg = document.getElementById("message");    let div = document.getElementById('game');     //获取输入的宽高和地雷数    createContent = function (){            // console.log(xy.value);            // console.log(boo.value);            let xyNum = xy.value;             let booNum = boo.value;             // console.log(Math.pow(xyNum,2));                        //判断输入是否合法            if(Math.pow(xyNum,2)<boo.value){                meg.style.display = 'block';            }            else {//绘制地图                div.innerHTML = '';//清除上次div里的地图                let game = new Game('game',xyNum,booNum);            }        }    </script></body>

lei.js

Array.prototype.myindexOf = function(arr){    for(let i=0;i<this.length;i++){                if((this[i][0] == arr[0]) &&(this[i][1]==arr[1])){            return i;        }    }    return -1;} class Game {    constructor(id,xyNum,booNum){        this.xyNum = xyNum;        this.booNum = booNum;        this.id = id;         this.booArrs = [];//保存雷的位置        this.boox = -1;//地雷在x轴第几个块        this.booy = -1;//地雷在x轴第几个块         this.numArrs = [];//保存提醒数字的位置以及数字        this.num = 0;//保存找到的提醒数字的个数         this.markArrs = [];//保存标记位置的数组         //单个块的宽高        this.divw = 20;         // 初始化画布        this.initCanvas(xyNum);        // 初始化地雷位置(地雷用-1代替,图片绘制麻烦)        this.initBooxy(xyNum,booNum);        // 初始化遮挡物        this.initObs(xyNum);         //判断是否胜利        this.win();         }         initCanvas(xyNum){        this.canvas = document.createElement('canvas');        this.ctx = this.canvas.getContext('2d');                //1为border        this.canvas.width = (this.divw+1)*xyNum;        this.canvas.height = (this.divw+1)*xyNum;         // 绘制网格坐标                // 获取canvas的宽高;                let w = this.canvas.width;                let h = this.canvas.height;                // 绘制水平线                for (let i = 1; i < h / 21; i++) {                    this.ctx.beginPath();                    this.ctx.lineTo(0,  21 * i) //起点                    this.ctx.lineTo(w, 21 * i); //重点                    this.ctx.stroke();                        }                // h绘制垂直线                for (let i = 1; i < w / 21; i++) {                    this.ctx.beginPath();                    this.ctx.lineTo(21 * i,0) //起点                    this.ctx.lineTo(21 * i,h); //重点                    this.ctx.stroke();                        }                // ctx.stroke();         // 放入容器        this.div = document.getElementById(this.id);        this.div.appendChild(this.canvas);                // 绑定点击事件!!!        this.canvas.addEventListener('mousedown',this.mouseDown.bind(this))//!!!!注意需要更改this指向,用bind                // 清除鼠标右键的默认事件 “contextmenu“        this.canvas.addEventListener("contextmenu",function(event){            event.preventDefault()        })    }          initBooxy (xyNum,booNum){         // 随机地雷位置 并保存起来        for(let i=0;i<booNum;i++){             // x,y为地雷所在格子坐标,从0开始            this.boox = parseInt(Math.random()*xyNum);            this.booy = parseInt(Math.random()*xyNum);             //避免雷的位置重复            while(this.booArrs.myindexOf([this.boox,this.booy])!=-1){                this.boox = parseInt(Math.random()*xyNum);                this.booy = parseInt(Math.random()*xyNum);            }             this.booArrs.push([this.boox,this.booy])//!!!保存地雷的位置            console.log(i,'x:'+this.boox,'y:'+this.booy);             //绘制地雷            this.ctx.beginPath();//不清楚可不可以删            this.ctx.rect(this.boox*21,this.booy*21,20,20);            this.ctx.fillStyle = 'red';            this.ctx.fill();        }         // 绘制地雷位置周围提醒数字            // 这里的逻辑可以优化,不提前绘制数字,在点击清除障碍物后再判断绘制。                        // 这里为法二       for(let i=0;i<xyNum;i++){           for(let j=0;j<xyNum;j++){               let num = 0;//提醒数字 ,每次重置为0                            if(this.booArrs.myindexOf([i-1,j-1]) !=-1){                    num++;                }                if(this.booArrs.myindexOf([i-1,j]) !=-1){                    num++;                }                if(this.booArrs.myindexOf([i-1,j+1]) !=-1){                    num++;                }                if(this.booArrs.myindexOf([i,j-1]) !=-1){                    num++;                }                if(this.booArrs.myindexOf([i,j+1]) !=-1){                    num++;                }                if(this.booArrs.myindexOf([i+1,j-1]) !=-1){                    num++;                }                if(this.booArrs.myindexOf([i+1,j]) !=-1){                    num++;                }                if(this.booArrs.myindexOf([i+1,j+1]) !=-1){                    num++;                }                               //绘制提醒数字                  if(num!=0 && (this.booArrs.myindexOf([i,j]) ==-1 )){//(this.booArrs.myindexOf([i,j]) ==-1)地雷不标注提示数字若。要标注需要+1(本身)                 this.ctx.font = '18px fasdg'                this.ctx.fillStyle = '#000'                this.ctx.fillText(num,i*(this.divw+1)+2,(j+1)*(this.divw+1)-2);//加1和j+1为测试结果,-+2是为了文本在格子里居中//y为文本中线坐标                                this.numArrs.push([i,j,num]);//i,j为提醒数字的块坐标,num为装数组里的值(myindexOf来判断)                }                // this.NUM = num;           }       }                     }         initObs(xyNum){    for(let i=0;i<xyNum;i++){        for(let j=0;j<xyNum;j++){             this.ctx.beginPath();            this.ctx.rect(i*21,j*21,20,20);            // this.ctx.fillStyle = 'rgb(155,25,205,0.7)';//设置障碍物透明度可以方便查看雷的位置            this.ctx.fillStyle = 'rgb(155,25,205,1)';//正常游戏时透明度为'1‘            this.ctx.fill();        }    }   }       mouseDown(){     //这里使用preventDefault,默认事件被没有消除,是因为触发鼠标右键的默认事件的事件类型不是mousedown,是contextmenu    // event.preventDefault(); //ie9以下不兼容         this.clix = Math.floor(event.layerX/( this.divw+1));//this.divw为20是块的宽    this.cliy = Math.floor(event.layerY/( this.divw+1));      // 鼠标左键    if(event.button==0){        this.clearObs(this.clix,this.cliy);      }        // 鼠标右键    else if(event.button==2){                        this.markObs(this.clix,this.cliy);    }      }       //这里的代码可以封装一下 为了方便此处没有封装   clearObs(x,y){    // console.log(x,y);点击坐标     this.ctx.clearRect(x*21,y*21,20,20);//清除指定块        // 点击到标记,点击到提醒数字,点击到地雷,点击到空白,    if(this.markArrs.myindexOf([x,y])!=-1){  //点击到标记,重新覆盖        this.ctx.rect(x*21,y*21,20,20);        this.ctx.fillStyle = 'rgb(155,25,205,1)';        this.ctx.fill();                this.ctx.beginPath();        this.ctx.fillStyle = 'red';        this.ctx.fillText('?',x*(this.divw+1)+2,(y+1)*(this.divw+1)-2);        this.ctx.fill();     }    else if(this.numArrs.myindexOf([x,y])!=-1){//点击到提醒数字        let index = this.numArrs.myindexOf([x,y]);//下标        let num = this.numArrs[index][2];//提醒数字        this.ctx.fillText(num,x*(this.divw+1)+2,(y+1)*(this.divw+1)-2);//加1和j+1为测试结果,-+2是为了文本在格子里居中//y为文本中线坐标        this.num++;    }    else if(this.booArrs.myindexOf([x,y])!=-1){//,点击到地雷,全部绘制        console.log(this.booArrs.myindexOf([x,y]));            //绘制全图            // 绘制提醒数字            for(let i=0;i<this.xyNum;i++){                for(let j=0;j<this.xyNum;j++){                    let num = 0;//提醒数字 ,每次重置为0                     // if(booArrs.indexof([i-1,j-1]) != -1){//数组是对象这样永远-1                     this.ctx.clearRect(i*21,j*21,20,20);                     if(this.booArrs.myindexOf([i-1,j-1]) !=-1){                         num++;                     }                     if(this.booArrs.myindexOf([i-1,j]) !=-1){                         num++;                     }                     if(this.booArrs.myindexOf([i-1,j+1]) !=-1){                         num++;                     }                     if(this.booArrs.myindexOf([i,j-1]) !=-1){                         num++;                     }                     if(this.booArrs.myindexOf([i,j+1]) !=-1){                         num++;                     }                     if(this.booArrs.myindexOf([i+1,j-1]) !=-1){                         num++;                     }                     if(this.booArrs.myindexOf([i+1,j]) !=-1){                         num++;                     }                     if(this.booArrs.myindexOf([i+1,j+1]) !=-1){                         num++;                     }                                                                   //绘制提醒数字                     if(num!=0 && (this.booArrs.myindexOf([i,j]) ==-1 )){//(this.booArrs.myindexOf([i,j]) ==-1)地雷不标注提示数字若要标注需要+1(本身)                          this.ctx.font = '18px fasdg'                     this.ctx.fillStyle = '#000'                     this.ctx.fillText(num,i*(this.divw+1)+2,(j+1)*(this.divw+1)-2);//加1和j+1为测试结果,-+2是为了文本在格子里居中//y为文本中线坐标                                          this.numArrs.push([i,j,num]);//i,j为提醒数字的块坐标,num为装数组里的值(myindexOf来判断)                     }                     // this.NUM = num;                }            }            // 绘制地雷            for(let i=0;i<this.booArrs.length;i++){                this.ctx.fillStyle = 'red';                this.ctx.rect(this.booArrs[i][0]*21,this.booArrs[i][1]*21,20,20);                this.ctx.fill();             }            this.ctx.clearRect((this.xyNum-1)*21,(this.xyNum-1)*21,20,20);//每次最后一个都会变红,不知道原因,此处专门删除。                       alert('你惊动了雷雷');                 }     else {         this.isboo(this.ctx,x,y,this.booArrs,this.numArrs,this.markArrs,this.xyNum);      }    }    win (){//标记数组==地雷数组    this.tim = setInterval(()=>{        if(this.booArrs.length ==this.markArrs.length){            for(let i=0;i<this.booNum;i++){                                if( true == this.booArrs.some(()=>{                    return this.markArrs.myindexOf(this.booArrs[i])!=-1;                })){                   this.booNum--;                }                if(this.booNum==0){                    clearInterval(this.tim);                    alert('you are win');                    }            }        }    },10)    }   isboo(ctx,x,y,booArrs,numArrs,markArrs,xyNum){       new Isboo(ctx,x,y,booArrs,numArrs,markArrs,xyNum);   }         markObs(x,y){         console.log(x,y);    new MarkObs(this.ctx,x,y,this.booArrs,this.divw,this.markArrs);        }    }

isboo.js

Array.prototype.myindexOf = function(arr){    for(let i=0;i<this.length;i++){                if((this[i][0] == arr[0]) &&(this[i][1]==arr[1])){            return i;        }    }    return -1;} class Isboo {    constructor(ctx,x,y,booArrs,numArrs,markArrs,xyNum){        this.x = x;        this.y = y;                // 判断有没有提醒数字        this.isbool(ctx,x,y,booArrs,numArrs,markArrs,xyNum);        this.isboor(ctx,x,y,booArrs,numArrs,markArrs,xyNum);        this.isboot(ctx,x,y,booArrs,numArrs,markArrs,xyNum);        this.isboob(ctx,x,y,booArrs,numArrs,markArrs,xyNum);    }    isbool(ctx,x,y,booArrs,numArrs,markArrs,xyNum){        if((numArrs.myindexOf([x,y])==-1)&&(x<xyNum)&&(markArrs.myindexOf([x,y])==-1)){            ctx.clearRect(x*21,y*21,20,20);            x+=1;            this.isbool(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            // this.isboor(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            this.isboot(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            this.isboob(ctx,x,y,booArrs,numArrs,markArrs,xyNum);        }else {            return ;        }    }    isboor(ctx,x,y,booArrs,numArrs,markArrs,xyNum){        if((numArrs.myindexOf([x,y])==-1)&&(x>=0)&&(markArrs.myindexOf([x,y])==-1)){            ctx.clearRect(x*21,y*21,20,20);            x-=1;            // this.isbool(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            this.isboor(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            this.isboot(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            this.isboob(ctx,x,y,booArrs,numArrs,markArrs,xyNum);        }else {            return ;        }    }    isboot(ctx,x,y,booArrs,numArrs,markArrs,xyNum){        if((numArrs.myindexOf([x,y])==-1)&&(y<xyNum)&&(markArrs.myindexOf([x,y])==-1)){            ctx.clearRect(x*21,y*21,20,20);            y+=1;            // this.isbool(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            // this.isboor(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            this.isboot(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            // this.isboob(ctx,x,y,booArrs,numArrs,markArrs,xyNum);         }else {            return ;        }    }    isboob(ctx,x,y,booArrs,numArrs,markArrs,xyNum){        if((numArrs.myindexOf([x,y])==-1)&&(y>=0)&&(markArrs.myindexOf([x,y])==-1)){            ctx.clearRect(x*21,y*21,20,20);            y-=1;            // this.isbool(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            // this.isboor(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            // this.isboot(ctx,x,y,booArrs,numArrs,markArrs,xyNum);            this.isboob(ctx,x,y,booArrs,numArrs,markArrs,xyNum);        }else {            return ;        }    } }

MarkObs.js

Array.prototype.myindexOf = function(arr){    for(let i=0;i<this.length;i++){                if((this[i][0] == arr[0]) &&(this[i][1]==arr[1])){            return i;        }    }    return -1;} class MarkObs{    constructor(ctx,x,y,booArrs,divw,markarrs){        this.markObs(ctx,x,y,booArrs,divw,markarrs);    }     markObs(ctx,x,y,booArrs,divw,markarrs){         if(markarrs.myindexOf([x,y])==-1){//如果标记数组里没有该地址,则标记,并添加进数组        ctx.beginPath();        ctx.fillStyle = 'red';        ctx.fillText('?',x*(divw+1)+2,(y+1)*(divw+1)-2);        markarrs.push([x,y]);        }else {//如果标记数组里有该地址,则取消标记,并从数组中删除            ctx.clearRect(x*(divw+1),y*(divw+1),divw,divw);            ctx.beginPath();            ctx.rect(x*21,y*21,20,20);            ctx.fillStyle = 'rgb(155,25,205,1)';            ctx.fill();            markarrs.splice((markarrs.myindexOf([x,y])),1);        }    } }

页面效果

初始化障碍物设置了透明度时

如何利用js+canvas实现扫雷游戏

正常游戏时

这里点击右键标记后忘了把填充颜色设置回来。所以后面变红。

如何利用js+canvas实现扫雷游戏

关于“如何利用js+canvas实现扫雷游戏”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯