【js小游戏&案例】纯前端实现算法迷宫、飞机大战、贪吃蛇小游戏,附带源码

迷宫

效果图:

序幕:

迷宫渲染:定义一个二维数组、根据定义二维数组中的数字来标识不同的东西(障碍物、人物、终点、草地),然后两重循环将其渲染到页面

移动人物:给人物定义position: relative;,然后根据left,bottom来移动

完整代码:

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>幼儿迷宫</title>
</head>
<style>
  table {
    border-collapse: collapse;
    padding: 0;
    background: url("./gass.jpg");
    width: 64%;
    height: 100%;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
  }

  td {
    width: 80px;
    height: 80px;
  }

  tr {
    display: block;
    margin: -5px;
  }
</style>

<body onload="init()" onkeypress="keypress(event)">
  <table id="map">
  </table>
  <script>
    var empty = 0;   //空地或草坪
    var stone = 1;   //石头的标记是1
    var panda = 9;   //熊猫的标记是9
    var point = 6;   //终点
    var stepNumber = 0 //步数

    var mapData = [
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      [1, 0, 1, 0, 0, 0, 1, 0, 6, 1],
      [1, 0, 0, 0, 1, 0, 1, 0, 1, 1],
      [1, 1, 1, 1, 1, 0, 1, 0, 0, 1],
      [1, 1, 1, 0, 0, 0, 0, 1, 0, 1],
      [1, 0, 0, 0, 1, 1, 1, 1, 0, 1],
      [1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
      [1, 0, 0, 1, 1, 1, 0, 1, 0, 1],
      [1, 1, 0, 0, 0, 0, 0, 1, 0, 1],
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    ]
    var initPoint = [1, 1];   //初始化熊猫的位置

    var row = mapData.length;  //地图的行
    var column = mapData[0].length;  //地图的列

    function init() {
      //二维数组里,去初始化熊猫的位置
      mapData[initPoint[0]][initPoint[1]] = panda
      loadData(mapData);
    }

    /**
      *  渲染地图
      */
    function loadData(mapData) {
      // 获取地图对象
      var map = document.getElementById("map");

      //渲染一行八列的数据
      var mapHTML = "";
      for (var i = 0; i < row; i++) {
        mapHTML += "<tr>";
        for (var j = 0; j < column; j++) {
          if (mapData[i][j] == 0) {
            mapHTML += "<td></td>";
          } else if (mapData[i][j] == 1) {
            mapHTML += '<td><img src="./qualityControl.png" style="height: 80px; height: 80px; border-radius: 50%;" ></td>';
          } else if (mapData[i][j] == 9) {
            mapHTML += '<td><img src="./location.png" id="panda" style="height: 80px; height: 80px; border-radius: 50%;position: relative;" ></td>';
          }else if (mapData[i][j] == 6) {
            mapHTML += '<td><img src="./endPoint.png" id="panda" style="height: 80px; height: 80px; border-radius: 50%;position: relative;" ></td>';
          }
        }
        mapHTML += "</tr>";
      }
      map.innerHTML = mapHTML;
    }
    /**
       * 障碍检测(可加多个障碍条件)
       * @param yPoint
       * @param xPoint
       * @returns {boolean}
       */
    function checkStone(yPoint, xPoint) {
      return mapData[yPoint][xPoint] == stone;
    }
    // 移动方法
    function move(keynum, Value) {
      stepNumber++
      var pandaPos = document.getElementById("panda")
      if (119 == keynum) {
        const result = Value + 80;
        pandaPos.style.bottom = result + "px";
      } else if (100 == keynum) {
        const result = Value + 80;
        pandaPos.style.left = result + "px";
      } else if (115 == keynum) {
        const result = Value - 80;
        pandaPos.style.bottom = result + "px";
      } else if (97 == keynum) {
        const result = Value - 80;

        pandaPos.style.left = result + "px";
      }

    }
    /**
       * 监听wasd按键事件:w(上) s(下) a(左) d(右)
       * @param e
       */
    var keypress = function keypress(e) {
      var pandaPos = document.getElementById("panda")
      var keynum = window.event ? e.keyCode : e.which;
      if (119 == keynum) {
        var point = initPoint;
        if (point[0] < row - 1) {
          var xPoint = initPoint[1];
          var yPoint = initPoint[0] - 1;
          if (checkStone(yPoint, xPoint)) {
            console.log("碰撞到石头了,停止动作")
            return
          }
          // console.log("移动后的位置:x:" + xPoint + " , y:" + yPoint)

          initPoint = [yPoint, xPoint]
          // operatePanda(point);
          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.bottom;
          const Value = parseInt(ValueStr, 10);
          move(119, Value)
          console.log("向上")
        } else {
          console.log("超出地图范围了,停止动作")
        }
      } else if (97 == keynum) {
        var point = initPoint;
        if (point[1] > 0) {

          var xPoint = initPoint[1] - 1;
          var yPoint = initPoint[0];

          if (checkStone(yPoint, xPoint)) {
            console.log("碰撞到石头了,停止动作")
            return
          }
          // console.log("移动后的位置:x:" + xPoint + " , y:" + yPoint)
          initPoint = [yPoint, xPoint]
          // operatePanda(point);
          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.left;
          const Value = parseInt(ValueStr, 10);
          move(97, Value)
          console.log("向左")
        } else {
          console.log("超出地图范围了,停止动作")
        }

      } else if (115 == keynum) {

        var point = initPoint;
        if (point[0] < row - 1) {
          var xPoint = initPoint[1];
          var yPoint = initPoint[0] + 1;
          if (checkStone(yPoint, xPoint)) {
            console.log("碰撞到石头了,停止动作")
            return
          }
          // console.log("移动后的位置:x:" + xPoint + " , y:" + yPoint)

          initPoint = [yPoint, xPoint]
          // operatePanda(point);
          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.bottom;
          const Value = parseInt(ValueStr, 10);
          move(115, Value)

          console.log("向下")
        } else {
          console.log("超出地图范围了,停止动作")
        }

      } else if (100 == keynum) {

        var point = initPoint;
        if (point[1] < column - 1) {
          var xPoint = initPoint[1] + 1;
          var yPoint = initPoint[0];
          if (checkStone(yPoint, xPoint)) {
            console.log("碰撞到石头了,停止动作")
            return
          }
          // console.log("移动后的位置:x:" + xPoint + " , y:" + yPoint)
          initPoint = [yPoint, xPoint]
          // operatePanda(point);
          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.left;
          const Value = parseInt(ValueStr, 10);
          move(100, Value)
          console.log("向右")
        } else {
          console.log("超出地图范围了,停止动作")
        }
      }
      // console.log(initPoint)
      if(initPoint[0] == 1 && initPoint[1] == 8){
        console.log(`总共${stepNumber},到达终点`)
      }
    }
  </script>
</body>

</html>

迷宫2.0

在上面用js开发固定迷宫时就想,这样的死迷宫不能称之为迷宫,如何让这个迷宫动起来呢?

让浏览器每次刷新时,通过计算重新生成一个迷宫,但这样有个问题,每次动态生成的迷宫必须保证它是通路的,通过思考以及借鉴,决定使用深度优先算法来实现

保证渲染迷宫为通路后,然后将其中一个固定为终点,随机初始化一点为起点,这样说可能比较难懂放上一张图,接着描述

实现:

首先定义一个二维数组,初始化全都为1,然后随机一个位置作为初始通路,设置为0,效果如下:

然后通过上下左右加减一,来判断下一步的四周边是什么情况(是通路还是墙壁)。如果加减一的下一步在二维数组范围内且位置是墙壁,我们就再来一次上下左右加减下一步,再次了解下下一步四周的情况,如果四周有且只有一条通路,就可以将这个点设置为通路,意思如下如:

 因为是递归循环,如果碰到墙壁无法走通时,某一循环就结束,继续某一循环的上一级循环开始

 代码大致如下:

    // 深度优先搜索算法
    function generatePath(maze, row, col) {
      const directions = [[-1, 0], [0, 1], [1, 0], [0, -1]]; // 上、右、下、左

      // 解构赋值,遍历随机化后的方向数组
      for (const [dx, dy] of directions) {
        const newRow = row + dx; // 计算新位置的行号
        const newCol = col + dy; // 计算新位置的列号

        // 检查新位置是否在迷宫范围内且为墙壁
        if (
          newRow >= 0 && newRow < maze.length &&
          newCol >= 0 && newCol < maze[0].length &&
          maze[newRow][newCol] === 1
        ) {
          // 检查新位置的上、下、左、右四个方向的邻居是否是通路
          let count = 0; // 记录有多少个邻居是通路
          for (const [dirX, dirY] of directions) {
            const neighborRow = newRow + dirX; // 计算邻居位置的行号
            const neighborCol = newCol + dirY; // 计算邻居位置的列号

            // 检查邻居是否在迷宫范围内且为通路
            if (
              neighborRow >= 0 && neighborRow < maze.length &&
              neighborCol >= 0 && neighborCol < maze[0].length &&
              maze[neighborRow][neighborCol] === 0
            ) {
              count++; // 如果是通路,计数加一
            }
          }

          // 如果有且仅有一个邻居是通路,则将新位置设为通路
          if (count === 1) {
            // console.log(newRow,newCol)
            maze[newRow][newCol] = 0; // 将新位置设为通路
            generatePath(maze, newRow, newCol); // 递归调用生成路径,继续向新位置探索
          }
        }
      }
    }

但是directions = [[-1, 0], [0, 1], [1, 0], [0, -1]]规定死的,这样生成的迷宫大致图形就相差不大,如果每次递归时,将这个数据打乱,效果就很棒了,如下代码:

    // 洗牌算法
    function shuffleArray(array) {
      for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
      }
    }

2.0完整代码:

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

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

  table {
    border-collapse: collapse;
    padding: 0;
    background: url("./gass.jpg");
    height: 100%;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
  }

  td {
    display: inline-block;
    padding: 0;
    width: 80px;
    height: 80px;
  }

  tr {
    display: block;
  }
</style>

<body onload="init()" onkeypress="keypress(event)">
  <table id="map">
  </table>
  <script>
    var initPoint = null
    // var mapPoint = null
    function generateMaze(rows, cols) {
      // 创建一个空的二维数组
      const maze = [];
      for (let i = 0; i < rows; i++) {
        maze[i] = [];
        for (let j = 0; j < cols; j++) {
          maze[i][j] = 1; // 初始化为墙壁(1)
        }
      }

      // 随机选择一个起始位置
      const startRow = Math.floor(Math.random() * rows);
      const startCol = Math.floor(Math.random() * cols);
      maze[startRow][startCol] = 0; // 起始位置设为通路(0)
      initPoint = [startRow, startCol]

      // 使用递归函数来生成迷宫 
      generatePath(maze, startRow, startCol);

      return maze;
    }

    // 深度优先搜索算法
    function generatePath(maze, row, col) {
      const directions = [[-1, 0], [0, 1], [1, 0], [0, -1]]; // 上、右、下、左
      shuffleArray(directions)

      // 解构赋值,遍历随机化后的方向数组
      for (const [dx, dy] of directions) {
        const newRow = row + dx; // 计算新位置的行号
        const newCol = col + dy; // 计算新位置的列号

        // 检查新位置是否在迷宫范围内且为墙壁
        if (
          newRow >= 0 && newRow < maze.length &&
          newCol >= 0 && newCol < maze[0].length &&
          maze[newRow][newCol] === 1
        ) {
          // 检查新位置的上、下、左、右四个方向的邻居是否是通路
          let count = 0; // 记录有多少个邻居是通路
          for (const [dirX, dirY] of directions) {
            const neighborRow = newRow + dirX; // 计算邻居位置的行号
            const neighborCol = newCol + dirY; // 计算邻居位置的列号

            // 检查邻居是否在迷宫范围内且为通路
            if (
              neighborRow >= 0 && neighborRow < maze.length &&
              neighborCol >= 0 && neighborCol < maze[0].length &&
              maze[neighborRow][neighborCol] === 0
            ) {
              count++; // 如果是通路,计数加一
            }
          }

          // 如果有且仅有一个邻居是通路,则将新位置设为通路
          if (count === 1) {
            // console.log(newRow,newCol)
            maze[newRow][newCol] = 0; // 将新位置设为通路
            generatePath(maze, newRow, newCol); // 递归调用生成路径,继续向新位置探索
          }
        }
      }
    }


    // 洗牌算法
    function shuffleArray(array) {
      for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
      }
    }

    // 使用示例
    const rows = 10;
    const cols = 10;
    const maze = generateMaze(rows, cols);

    var empty = 0;   //空地或草坪
    var stone = 1;   //石头的标记是1
    var panda = 8;   //熊猫的标记是9
    var point = 6;   //终点
    var stepNumber = 0 //步数

    var mapData = maze
    var mapPoint = [9, 9];   //初始化终点的位置

    var row = mapData.length;  //地图的行
    var column = mapData[0].length;  //地图的列

    function init() {
      //二维数组里,去初始化熊猫的位置
      mapData[initPoint[0]][initPoint[1]] = panda
      mapData[mapPoint[0]][mapPoint[1]] = point
      loadData(mapData);
    }

    /**
      *  渲染地图
      */
    function loadData(mapData) {
      // 获取地图对象
      var map = document.getElementById("map");

      //渲染一行八列的数据
      var mapHTML = "";
      for (var i = 0; i < row; i++) {
        mapHTML += "<tr>";
        for (var j = 0; j < column; j++) {
          if (mapData[i][j] == 0) {
            mapHTML += "<td></td>";
          } else if (mapData[i][j] == 1) {
            mapHTML += '<td><img src="./qualityControl.png" style="height: 80px; height: 80px; border-radius: 50%;" ></td>';
          } else if (mapData[i][j] == 8) {
            mapHTML += '<td><img src="./location.png" id="panda" style="height: 80px; height: 80px; border-radius: 50%;position: relative;" ></td>';
          } else if (mapData[i][j] == 6) {
            mapHTML += '<td><img src="./endPoint.png" id="panda" style="height: 80px; height: 80px; border-radius: 50%;position: relative;" ></td>';
          }
        }
        mapHTML += "</tr>";
      }
      map.innerHTML = mapHTML;
    }
    /**
       * 障碍检测(可加多个障碍条件)
       * @param yPoint
       * @param xPoint
       * @returns {boolean}
       */
    function checkStone(yPoint, xPoint) {
      if (yPoint < 0 || xPoint < 0 || yPoint >= mapData.length || xPoint  >= mapData[0].length) {
        return 2
      } else if (mapData[yPoint][xPoint] == stone) {
        return 1
      }
    }
    // 移动方法
    function move(keynum, Value) {
      stepNumber++
      var pandaPos = document.getElementById("panda")
      if (119 == keynum) {
        const result = Value + 80;
        pandaPos.style.bottom = result + "px";
      } else if (100 == keynum) {
        const result = Value + 80;
        pandaPos.style.left = result + "px";
      } else if (115 == keynum) {
        const result = Value - 80;
        pandaPos.style.bottom = result + "px";
      } else if (97 == keynum) {
        const result = Value - 80;

        pandaPos.style.left = result + "px";
      }

    }
    /**
       * 监听wasd按键事件:w(上) s(下) a(左) d(右)
       * @param e
       */
    var keypress = function keypress(e) {
      var pandaPos = document.getElementById("panda")
      var keynum = window.event ? e.keyCode : e.which;
      if (119 == keynum) {
        var point = initPoint;
        if (point[0] < row) {
          var xPoint = initPoint[1];
          var yPoint = initPoint[0] - 1;
          if (checkStone(yPoint, xPoint) == 1) {
            console.log("碰撞到石头了,停止动作")
            return
          } else if (checkStone(yPoint, xPoint) == 2) {
            console.log("超出边界了,停止动作")
            return
          }

          initPoint = [yPoint, xPoint]

          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.bottom;
          const Value = parseInt(ValueStr, 10);
          move(119, Value)
          console.log("向上")
        } else {
          console.log("超出地图范围了,停止动作")
        }
      } else if (97 == keynum) {
        var point = initPoint;
        if (point[1] > 0) {

          var xPoint = initPoint[1] - 1;
          var yPoint = initPoint[0];

          if (checkStone(yPoint, xPoint) == 1) {
            console.log("碰撞到石头了,停止动作")
            return
          } else if (checkStone(yPoint, xPoint) == 2) {
            console.log("超出边界了,停止动作")
            return
          }

          initPoint = [yPoint, xPoint]

          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.left;
          const Value = parseInt(ValueStr, 10);
          move(97, Value)
          console.log("向左")
        } else {
          console.log("超出地图范围了,停止动作")
        }

      } else if (115 == keynum) {

        var point = initPoint;
        if (point[0] < row) {
          var xPoint = initPoint[1];
          var yPoint = initPoint[0] + 1;
          if (checkStone(yPoint, xPoint) == 1) {
            console.log("碰撞到石头了,停止动作")
            return
          } else if (checkStone(yPoint, xPoint) == 2) {
            console.log("超出边界了,停止动作")
            return
          }
          initPoint = [yPoint, xPoint]

          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.bottom;
          const Value = parseInt(ValueStr, 10);
          move(115, Value)

          console.log("向下")
        } else {
          console.log("超出地图范围了,停止动作")
        }

      } else if (100 == keynum) {

        var point = initPoint;
        if (point[1] < column - 1) {
          var xPoint = initPoint[1] + 1;
          var yPoint = initPoint[0];
          if (checkStone(yPoint, xPoint) == 1) {
            console.log("碰撞到石头了,停止动作")
            return
          } else if (checkStone(yPoint, xPoint) == 2) {
            console.log("超出边界了,停止动作")
            return
          }
          initPoint = [yPoint, xPoint]

          const style = window.getComputedStyle(pandaPos);
          const ValueStr = style.left;
          const Value = parseInt(ValueStr, 10);
          move(100, Value)
          console.log("向右")
        } else {
          console.log("超出地图范围了,停止动作")
        }
      }

      if (initPoint[0] == 9 && initPoint[1] == 9) {
        console.log()
        alert(`总共${stepNumber},到达终点`);
        location.reload();
      }
    }
  </script>
</body>

</html>

上述如何表达错误或者不明白的,可以评论留言一起讨论

飞机大战

技能:

html js css

需求:

我军飞机左右移动,并且按空格 可以发射炮弹,敌方飞机从上往下飞,接触到我方炮弹 飞机被歼灭,我军接触到敌军飞机也将会被歼灭,我方飞机吃到道具可以增加弹道

思路:

初始化:初始创建我方飞机,用定时器定时创建敌方飞机,敌方飞机位置由random()随机生成

移动:飞机样式使用position: absolute,然后监听键盘被按事件 用left位置来移动我方飞机,敌军飞机也有定时器 定时向下移动

发射炮弹:创建炮弹跟创建敌军类似,区别是炮弹的位置是根据我方飞机的位置进行初始化的,而敌军飞机位置是随机的

敌军歼灭:通过getBoundingClientRect()获取位置信息 来判断是否接触到;我军歼灭类似

能量小球:产生小球跟敌军类似,休改下样式就好了

效果图:

 全部代码:

<!DOCTYPE html>
<html>

<head>
    <title>飞机大战</title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
        }

        /* 游戏边框 */
        .game-board {
            width: 400px;
            height: 600px;
            border: 1px solid black;
            position: relative;
            overflow: hidden;
        }

        /* 我方飞机 */
        .player {
            width: 50px;
            height: 50px;
            background-color: blue;
            position: absolute;
            bottom: 0;
        }

        /* 敌方飞机 */
        .enemy {
            width: 30px;
            height: 30px;
            background-color: red;
            position: absolute;
            top: 0;
        }

        /* 子弹 */
        .bullet {
            width: 2px;
            height: 10px;
            background-color: black;
            position: absolute;
        }

        /* 分数 */
        .score {
            font-size: 18px;
            position: absolute;
            top: 10px;
            right: 10px;
        }

        /* 能量小球 */
        .power-up {
            width: 20px;
            height: 20px;
            background-color: green;
            position: absolute;
        }
    </style>
</head>

<body>
    <div class="game-board" id="game-board">
        <div class="score">分数: <span id="score">0</span></div>
    </div>

    <script>
        const gameBoard = document.getElementById('game-board');
        // 创建我方飞机
        const player = document.createElement('div');
        // 我军飞机初始位置
        let playerLeft = 175;
        player.style.left = `${playerLeft}px`;
        // 游戏是否结束
        let isGameOver = false;
        // 是否在发射子弹的状态
        let isShooting = false;
        // 初始化分数
        let score = 0;
        const scoreElement = document.getElementById('score');
        // 初始化我军飞机样式
        player.classList.add('player');
        gameBoard.appendChild(player);

        // 创造敌军飞机
        function createEnemy() {
            const enemy = document.createElement('div');
            // 随机位置
            let enemyLeft = Math.floor(Math.random() * 350);
            enemy.classList.add('enemy');
            enemy.style.left = `${enemyLeft}px`;
            gameBoard.appendChild(enemy);
            moveEnemy(enemy);
        }

        // 每30ms敌军就会向下移动五像素,如果到达边界就会取消移动 并且移除样式
        function moveEnemy(enemy) {
            let enemyTop = 0;
            let enemyInterval = setInterval(() => {
                if (enemyTop > 600 || isGameOver) {
                    clearInterval(enemyInterval);
                    // 出界 清除dom
                    gameBoard.removeChild(enemy);
                }

                enemy.style.top = `${enemyTop}px`;
                enemyTop += 5;

                // 如果两个接触到 就结束游戏
                if (checkCollision(player, enemy)) {
                    gameOver();
                }
            }, 30);
        }

        // 创建能量小球
        function createPowerUp() {
            const powerUp = document.createElement('div');
            // 随机水平位置
            let powerUpLeft = Math.floor(Math.random() * 350);
            powerUp.classList.add('power-up');
            powerUp.style.left = `${powerUpLeft}px`;
            gameBoard.appendChild(powerUp);

            movePowerUp(powerUp);
        }

        // 移动绿色小球
        // 每30ms绿色小球就会向下移动五像素,如果到达边界就会取消移动 并且移除样式
        function movePowerUp(powerUp) {
            let powerUpTop = 0;
            let powerUpInterval = setInterval(() => {
                if (powerUpTop > 600 || isGameOver) {
                    clearInterval(powerUpInterval);
                    // 出界 清除dom
                    gameBoard.removeChild(powerUp);
                }

                powerUp.style.top = `${powerUpTop}px`;
                powerUpTop += 5;

                // 我方飞机吃到绿色小球
                if (checkCollision(player, powerUp)) {
                    gameBoard.removeChild(powerUp);
                    powerUpEffect();
                }
            }, 30);
        }


        let isDoubleBullet = false;
        let numBullets = 1;

        // 新增函数,处理绿色小球效果
        function powerUpEffect() {
            isDoubleBullet = true;
            numBullets++;
        }

        // 创造子弹
        function createBullet(left, bottom) {
            // console.log(left, bottom)
            const bullet = document.createElement('div');
            bullet.classList.add('bullet');
            bullet.style.left = `${left + 25}px`;
            bullet.style.bottom = `50px`;
            gameBoard.appendChild(bullet);

            moveBullet(bullet);
        }
        // 子弹移动
        function moveBullet(bullet) {
            let bulletBottom = 50;
            let bulletInterval = setInterval(() => {
                if (bulletBottom > 600 || isGameOver) {
                    clearInterval(bulletInterval);
                    gameBoard.removeChild(bullet);
                }

                bullet.style.bottom = `${bulletBottom}px`;
                bulletBottom += 10;

                const enemies = document.querySelectorAll('.enemy');
                // 判断子弹是否接触到敌军飞机
                enemies.forEach(enemy => {
                    if (checkCollision(bullet, enemy)) {
                        clearInterval(bulletInterval);
                        gameBoard.removeChild(bullet);
                        gameBoard.removeChild(enemy);
                        increaseScore();
                    }
                });
            }, 30);
        }

        // 检测是否接触
        function checkCollision(a, b) {
            // getBoundingClientRect()是一个DOM元素对象的方法,用于获取该元素相对于视口(viewport)的位置和尺寸信息
            // 从而进行碰撞检测、位置计算等操作
            const aRect = a.getBoundingClientRect();
            const bRect = b.getBoundingClientRect();

            return !(
                // 我军飞机的top如果小于敌军的bottom 说明已经两机已经接触了
                aRect.top > bRect.bottom ||
                aRect.bottom < bRect.top ||
                aRect.right < bRect.left ||
                aRect.left > bRect.right
            );
        }

        // 游戏结束
        function gameOver() {
            isGameOver = true;
            // 移除gameBoard元素的所有子元素,即清空游戏面板
            while (gameBoard.firstChild) {
                gameBoard.removeChild(gameBoard.firstChild);
            }
            // 停止创造敌军飞机
            clearInterval(enemyInterval)
            gameBoard.innerHTML = '游戏结束!最终分数为: ' + score;
        }

        // 分数增加
        function increaseScore() {
            score++;
            scoreElement.textContent = score;

            if (score % 8 === 0) { // 每射杀十个敌方飞机,创建绿色小球
                createPowerUp();
            }
        }

        document.addEventListener('keydown', function (event) {
            // 空格键
            if (event.keyCode === 32 && !isGameOver) {
                // 发射与停止发射子弹
                if (!isShooting) {
                    isShooting = true;
                    shoot();
                }
            } else if (event.keyCode === 37) { // 左箭头键
                if (playerLeft > 0) playerLeft -= 10;
            } else if (event.keyCode === 39) { // 右箭头键
                if (playerLeft < 350) playerLeft += 10;
            }

            player.style.left = playerLeft + 'px';
        });

        // 控制发射子弹
        function shoot() {
            if (isShooting) {
                const plane = document.querySelector('.player');
                const planeLeft = parseInt(plane.style.left);
                const planeBottom = parseInt(plane.style.bottom);
                console.log(plane.style.left)

                // 增加子弹
                for (let i = 0; i < numBullets; i++) {
                    if (isDoubleBullet) {
                        createBullet(planeLeft - 5 + 10 * i , planeBottom);
                    } else {
                        createBullet(planeLeft, planeBottom);
                    }
                }
                // 设置发射子弹的频率,这里是每隔200毫秒发射一颗子弹
                setTimeout(shoot, 200);
            }
        }

        document.addEventListener('keyup', function (event) {
            if (event.keyCode === 32) { // 当松开空格键时,关闭发射状态
                isShooting = false;
            }
        });

        let enemyInterval = setInterval(createEnemy, 1000); // 每隔1秒创建一个敌方飞机
    </script>
</body>

</html>

有其他问题 欢迎大家评论留言

贪吃蛇

效果图:

完整代码展示:

index.html

<!DOCTYPE html>
<html>

<head>
    <title>贪吃蛇游戏</title>
    <style>
        #game-board {
            width: 400px;
            height: 400px;
            border: 1px solid #000;
            position: relative;
        } 

        .snake-dot,
        .food-dot {
            width: 20px;
            height: 20px;
            background-color: #000;
            position: absolute;
        }

        .food-dot {
            background-color: red;
        }
    </style>
</head>

<body>
    <div id="score">得分: 0</div>
    <div id="game-board"></div>

    <script src="./js/snake.js"></script>
</body>

</html>

snake.js

// 游戏设置
var tileSize = 20; // 单个方块的大小
var initialSpeed = 200; // 初始速度(毫秒)
var speedMultiplier = 1; // 加速倍数
var direction = "right"; // 初始方向

// 初始化贪吃蛇
var snake = [
    { top: 0, left: 0 },
];

// 当前食物的位置
var food = {};

// 游戏循环计时器
var gameLoop;

// 游戏是否结束
var gameOver = false;

// 游戏板元素
var gameBoard = document.getElementById("game-board");

// 监听方向键按下事件
document.addEventListener("keydown", function (event) {
    if (event.key === "ArrowUp" && direction !== "down") {
        direction = "up";
    } else if (event.key === "ArrowDown" && direction !== "up") {
        direction = "down";
    } else if (event.key === "ArrowLeft" && direction !== "right") {
        direction = "left";
    } else if (event.key === "ArrowRight" && direction !== "left") {
        direction = "right";
    }
});

// 开始游戏
function startGame() {
    createFood();
    document.getElementById("score").innerText = "得分: " + (snake.length - 1);
    gameLoop = setInterval(moveSnake, initialSpeed);
}

// 创建食物
function createFood() {
    // 随机食物的位置
    // gameBoard.offsetHeight 规定的宽高
    var top = Math.floor(Math.random() * (gameBoard.offsetHeight / tileSize)) * tileSize;
    var left = Math.floor(Math.random() * (gameBoard.offsetWidth / tileSize)) * tileSize;
    food = { top: top, left: left };

    // 创建
    var foodElement = document.createElement("div");
    foodElement.className = "food-dot";
    foodElement.style.top = food.top + "px";
    foodElement.style.left = food.left + "px";

    gameBoard.appendChild(foodElement);
}

// 移动贪吃蛇
function moveSnake() {
    // Object.assign() 用于将一个或多个源对象的属性复制到目标对象中
    var head = Object.assign({}, snake[0]);

    // 移动,通过定时器移动
    if (direction === "up") {
        head.top -= tileSize;
    } else if (direction === "down") {
        head.top += tileSize;
    } else if (direction === "left") {
        head.left -= tileSize;
    } else if (direction === "right") {
        head.left += tileSize;
    }

    if (checkCollision(head)) {
        gameOver = true;
        clearInterval(gameLoop);
        if (gameOver) {
            var restart = confirm("游戏结束!你的分数为:" + (snake.length - 1) + "。是否重新开始?");
            if (restart) {
                snake = [
                    { top: 0, left: 0 }
                ];
                direction = "right";
                initialSpeed = 200;
                gameOver = false;
                // 清除食物
                let foodElement = document.querySelector(".food-dot");
                gameBoard.removeChild(foodElement);
                startGame()
                return
            }
        }
    }

    //元素插入到数组的开头
    snake.unshift(head);

    if (head.top === food.top && head.left === food.left) {
        // 删除已经被吃掉的食物
        var foodElement = document.querySelector(".food-dot");
        gameBoard.removeChild(foodElement);
        // 创建新的食物
        createFood();
    } else {
        // 移除数组的最后一个元素
        snake.pop();
    }

    updateSnake();
}


// 更新贪吃蛇的显示
function updateSnake() {
    // 清除之前的位置
    var snakeDots = document.getElementsByClassName("snake-dot");
    while (snakeDots.length > 0) {
        snakeDots[0].parentNode.removeChild(snakeDots[0]);
    }

    // 绘制当前位置
    for (var i = 0; i < snake.length; i++) {
        var snakeElement = document.createElement("div");
        snakeElement.className = "snake-dot";
        snakeElement.style.top = snake[i].top + "px";
        snakeElement.style.left = snake[i].left + "px";
        gameBoard.appendChild(snakeElement);
    }
    document.getElementById("score").innerText = "得分: " + (snake.length - 1);
}

// 检查是否与边界或自身碰撞
function checkCollision(position) {
    // 是否与边界碰撞
    if (
        position.top < 0 ||
        position.top >= gameBoard.offsetHeight ||
        position.left < 0 ||
        position.left >= gameBoard.offsetWidth
    ) {
        return true;
    }
    // 自身碰撞
    for (var i = 1; i < snake.length; i++) {
        if (position.top === snake[i].top && position.left === snake[i].left) {
            return true;
        }
    }

    return false;
}

// 启动游戏
startGame();

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
web前端拼图小游戏是一个通过HTML、CSS和JavaScript实现游戏。你可以使用该游戏来学习和练习前端开发技术。该游戏的源代码可以在引用中找到,作者是博主青丝缠光阴。这个拼图游戏的画面精美,非常适合初学者学习使用。 如果你想要使用该游戏作为HTML网页设计结课作业的一部分,可以根据你的需要进行定制和修改。引用提供了一些常见的网页设计题材,你可以选择其中之一来设计你的网页。同时,引用中提供了一些代码实现,你可以根据需要进行参考和使用。 总的来说,这个web前端拼图小游戏是一个很好的学习和练习前端开发技术的工具,你可以根据自己的需求来使用和定制。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Web前端小实践之拼图游戏.rar](https://download.csdn.net/download/UZDW_/12755504)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [web前端期末大作业--HTML+CSS+JS实现美女拼图游戏](https://blog.csdn.net/BYGFJ/article/details/121869002)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自湖南的阿晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值