纯原生JS用面向对象class方法实现简易扫雷小游戏

Posted 我是真的不会前端

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了纯原生JS用面向对象class方法实现简易扫雷小游戏相关的知识,希望对你有一定的参考价值。

Demo介绍

纯原生js 实现 且用ES6语法class写的扫雷小游戏:布局为10*10的网格,随机生成10-20个雷。
左键点击扫雷。右键标记该地方有雷。该demo下载下来复制到html文件中直接可以使用。不需要下载任何插件和引入任何框架。

小游戏源代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    .container {
      width: 500px;
      height: 500px;
      background: rgb(150, 130, 130);
      position: relative;
      border: 5px solid #000;
      margin: 0 auto;

    }
  </style>
</head>

<body>
  <div class="container">

  </div>


  <script>
    class mineSweeping {
      constructor() {
        let container = this.$$(".container")
        this.container = container;
        this.mineWidth = 50;
        this.mineHeight = 50;
        this.mineNum =
          (this.container.clientWidth * this.container.clientHeight) /
          (this.mineWidth * this.mineHeight);
        this.brr = [-11, -10, -9, -1, 1, 9, 10, 11];//
        this.mineColNum = this.container.clientWidth / this.mineWidth;

        this.createDiv()
        this.createMine()
        this.setNum()
      }

      // 创建div;
      createDiv() {
        for (let i = 0; i < this.mineNum; i++) {
          let div = document.createElement("div");
          // 添加样式
          Object.assign(div.style, {
            width: this.mineWidth - 2 + "px",
            height: this.mineHeight - 2 + "px",
            backgroundColor: "#ccc",
            border: "1px solid #fff",
            position: "absolute",
            left: (i % this.mineColNum) * this.mineWidth + "px",
            top: parseInt(i / this.mineColNum) * this.mineHeight + "px",
            textAlign: "center",
            lineHeight: "50px",
            userSelect: "none"
          });
          // 添加自定义属性
          div.mine = false // 是否为雷
          div.show = false // 是否被点开
          // 添加索引
          div.index = i
          // 添加坐标
          div.pos = {
            x: parseInt(i / this.mineColNum) + 1,
            y: i % this.mineColNum + 1,
          }
          // 绑定事件
          div.onclick = this.clickFn.bind(this)//左键事件
          div.oncontextmenu = this.contextmenuFn//右键事件
          this.container.appendChild(div);//追加小盒子
        }
      }
      // 随机生成几个地雷
      createMine() {
        this.divs = this.container.children
        let arr = []
        for (let i = 0; i < this.getRandom(10, 20); i++) {
          let x = this.getRandom(1, 11)
          let y = this.getRandom(1, 11)
          //随机获得10-20个得并随机放置,将左边存进数组
          arr.push({
            x, y
          })
        }
        for (let j = 0; j < arr.length; j++) {
          Array.from(this.divs).forEach(v => {
            // console.log(v.pos); 
            if (arr[j].x === v.pos.x && arr[j].y === v.pos.y) {
              v.mine = true

            }
          })
        }
      }
      // 计算数字
      setNum() {
        for (let i = 0; i < this.divs.length; i++) {
          if (this.divs[i].mine) {
            continue;
          }
          let num = 0;
          for (var j = 0; j < this.brr.length; j++) {
            // 考虑最上面一行 - 不能-11 不能-10 不能-9
            if (i < this.mineColNum && (this.brr[j] === -11 || this.brr[j] === -10 || this.brr[j] === -9)) {
              continue;
            }
            // // 最左边一行过滤掉
            if (i % this.mineColNum === 0 && (this.brr[j] === -11 || this.brr[j] === -1 || this.brr[j] === 9)) {
              continue;
            }
            // 最下面一行
            if (parseInt(i / this.mineColNum) === this.mineColNum - 1 && (this.brr[j] === 9 || this.brr[j] === 10 || this.brr[j] === 11)) {
              continue;
            }
            // 最右边一行
            if (i % this.mineColNum === this.mineColNum - 1 && (this.brr[j] === -9 || this.brr[j] === 1 || this.brr[j] === 11)) {
              continue;
            }
            // console.log(this.divs[i + this.brr[j]].mine);
            if (this.divs[i + this.brr[j]].mine) {
              num++
              // console.log(num);
            }
          }
          // 把数字存起来
          this.divs[i].num = num
        }
      }
      //左键点击事件
      clickFn(e) {
        e = e || window.event;
        let target = e.target || e.srcElement;
        let divs = target.parentNode.children
        if (target.mine) {//这里注意 要加target
          for (let i = 0; i < divs.length; i++) {
            divs[i].innerText = '';
            divs[i].innerText = divs[i].num ? divs[i].num : ''
            divs[i].style.backgroundColor = '#0f0'
          }
          for (let i = 0; i < divs.length; i++) {
            if (divs[i].mine) {
              divs[i].style.backgroundColor = 'red';

              divs[i].innerText = '雷';

            }
          }
        } else {
          this.openAround(target.index)
        }
      }
      //右键点击事件标记雷
      contextmenuFn() {
        this.style.backgroundColor = 'blue'
        return false // 阻止默认事件
      }
      // 自动递归打开周围空格子
      openAround(index) {
        this.container.children[index].innerText = this.container.children[index].num
        this.container.children[index].style.backgroundColor = '#0f0'
        this.container.children[index].show = true
        if (this.container.children[index].num === 0) {
          this.container.children[index].style.backgroundColor = 'pink';
          for (let j = 0; j < this.brr.length; j++) {
            if (index < this.mineColNum && (this.brr[j] === -11 || this.brr[j] === -10 || this.brr[j] === -9)) {
              continue;
            }
            // // 最左边一行过滤掉
            if (index % this.mineColNum === 0 && (this.brr[j] === -11 || this.brr[j] === -1 || this.brr[j] === 9)) {
              continue;
            }
            // 最下面一行
            if (parseInt(index / this.mineColNum) === this.mineColNum - 1 && (this.brr[j] === 9 || this.brr[j] === 10 || this.brr[j] === 11)) {
              continue;
            }
            // 最右边一行
            if (index % this.mineColNum === this.mineColNum - 1 && (this.brr[j] === -9 || this.brr[j] === 1 || this.brr[j] === 11)) {
              continue;
            }

            if (this.container.children[index + this.brr[j]].show) {
              continue
            }
            this.openAround(index + this.brr[j])
          }
        }

      }
      // 获取随机数
      getRandom(min, max) {
        return Math.floor(Math.random() * (max - min) + min);
      }
      //获取节点
      $$(tag, all = false) {
        return all ? document.querySelectorAll(tag) : document.querySelector(tag);
      }
    };
    new mineSweeping;



  </script>
</body>

</html>

3.Demo代码分析

1.定义位置。动态创建div小格子。将格子动态设置样式。给盒子设置自定义属性mine属性判断是否是地雷属性。show则判断是否被打开
2. 给盒子坐标编号。同时根据坐标值随机生成10-20个地雷在随机位置。
3. 添加点击事件。左键点击扫雷。右键点击标记。同时追加盒子在画布container上
4. 给每个盒子计算数字。并且设置边缘检测。每个盒子8个位置 上 上右,上左,下,下右,下左的位置设置成一个brr[I]数组。周围有地雷。则数值加加。
5. 点击左键事件:点击盒子,点击后显示数字且变色。吧show属性改为true。如果是雷有mine属性其为true.直接打开所有盒子并游戏结束。否则调用展开方法openaround()
6. 展开方法其实是一个递归函数。通过检查是否是空是则继续检查四周几个位置是否为空。直到碰到周边有雷的位置。递归终止。这里要注意。展开的递归函数是要排除自己的,如果比如你向右递归。但改盒子的左边又是上一个递归的本身。那就会出现死循环。报错。

4.运行截图

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

5.还需要改进或者可与完善的点

  1. 没有游戏结束中止和计分设置。可以在循环判断是否触雷那里改写成promise。then,catch方法则判定游戏获胜或结束。
  2. 可与将展开函数进行改造。改成一种DFC(图中的深度优先算法)
  3. 边界检测调用了几次,可以将其封装起来
  4. 周围值数组值可以未来根据盒子实际大小动态生成。

6.总结

写这种demo虽然工作中完全用不了,但对于js原生写法的和逻辑的锻炼以及js初学者有所帮助。

以上是关于纯原生JS用面向对象class方法实现简易扫雷小游戏的主要内容,如果未能解决你的问题,请参考以下文章

纯原生JS面向对象构造函数方法实现贪吃蛇小游戏

原生JS实现简易扫雷游戏

原生JS实现简易扫雷游戏

原生JS实现简易扫雷游戏

原生JS实现简易扫雷游戏

练手WPF——扫雷小游戏的简易实现(中)