简单的贪吃蛇小游戏。使用的是纯原生 JavaScript 和 HTML ,CSS

这个小游戏是一时兴起。在暑假时写的 。不过当时还比较简陋。
后来开学后 。还没上课。就又拿出来改了改界面。让它看起来更美观,更像贪吃蛇一些。

先展示展示几张图吧。

开始前
在这里插入图片描述
开始咯
在这里插入图片描述
吃了些小老鼠后:
在这里插入图片描述
撞墙或撞到自己后
在这里插入图片描述

当时去做这个时,可能是突然的一个想法。但并不是要做一个完整的游戏出来。
所以这里并没有设置关卡模式有些细节也并不关注 。只是去关注了身体移动,吃食物,成长这些主要的问题。
当然,这些主要问题解决,增加其他功能就不是大问题了。

这里我就先说一下我的思路,有兴趣和想法的同学们可以自己先去尝试尝试,或者可以跟我交流交流。

我的思路

身体怎么移动

这是我第一个思考的问题。
我先想,如果只有一个身体节点,也就是一个小圆 。应该怎么去移动。控制方向。
当然这个就简单了。我们先定义一个变量,保存移动方向 。 再定义一个函数,根据方向控制节点的定时移动。 然后监听键盘点击事件。改变方向就可以了。

那么如果有多个节点呢?显然每个节点都使用一个函数去控制它移动是不切实际的。
我们可以联想到火车的节点,转哪由车头驾驶员控制。后面跟着跑就行了
在这里插入图片描述

那么蛇身体的其他节点也是同样。它不需要知道方向,它要做的很简单,就是前一个节点在哪,它走了,我就移动到它的位置就可以了。
那么除了头部,其他的节点通过一个跟屁股走的函数 就可以完成移动了。

身体怎么成长

成长是在吃到食物后。仅判断一下头节点是否于食物节点位置重合即可。
身体成长还是比较简单的,也就是在尾部增加一个身体节点,但不是一吃到食物就变长。而是原本的最后一个节点移开后再边长。出现的位置顶替了原最后节点的位置。

怎么判断死亡

这个问题也是比较简单的,也是判断头部节点位置的问题。主要撞到自己,也就是与身体的某部位的位置重合或者与墙壁的位置重合了,就结束游戏即可。

demo

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <link rel="shortcut icon" href="./img/favicon.ico" type="image/x-icon">
  <title>贪吃蛇</title>
  <link rel="stylesheet" href="./greedySnake.css">
</head>

<body>
  <!-- 墙 -->
  <div class="wall">
    <!-- 开始按钮 -->
    <div class="start" style="display:block">
      <div class="tips_con"><text>?</text>
        <ul class="tips">
          <li><strong>·</strong> 通过键盘的上下左右的键改变贪吃蛇移动的方向</li>
          <li><strong>·</strong> 长按 Shift 键可以加速,松开恢复</li>
          <li><strong>·</strong> 无关卡设置,触墙或身体会死亡</li>
        </ul>
      </div>
      <button onclick="startGame()">&nbsp;</button>
    </div>
    <!-- 蛇盒子 -->
    <div class="snake" style="display:block">
      <!-- <div class="snake-node" id="tryNode"></div> -->
    </div>

    <!-- 食物盒子 -->
    <div class="foods"></div>
    <!-- 得分 -->
    <div id="infoBar">
      <div>得分:<span id="score">0</span></div>
    </div>
    <!-- 音效 -->
    <audio src="./sounds/eat.wav">播放</audio>
  </div>

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

</html>```
#### CSS
```css
html,
body {
  padding: 0;
  margin: 0;
  background-color: #111d49;
  overflow: hidden;
  user-select: none;
}

/* 背景及边框 */
.wall {
  width: 980px;
  height: 580px;

  border: 30px solid transparent;
  -webkit-border-image: url(./img/wall.png) 30 30 round;
  border-image: url(./img/wall.png) 30 30 round;

  margin: 80px auto;
  background-color: #666bc6;
  position: relative;
}

/* 蛇的身体节点 */
.snake-node {
  position: absolute;
  transform: translate(-50%, -50%);

  width: 20px;
  height: 20px;

  border: 2px solid #010496;
  border-radius: 50%;

  background-color: #010496;

  opacity: 1;
}

/* 提高蛇头层级 */
.snake-node:nth-child(1) {
  z-index: 2;
}

/* 眼睛 */
.snake-node:nth-child(1)::before {
  content: "";
  position: absolute;
  top: 0;
  left: -5px;

  width: 7px;
  height: 7px;

  background-color: #000;
  border: 3px solid #fff;
  border-radius: 50%;
  z-index: 2;
}

/* 眼睛 */
.snake-node:nth-child(1)::after {
  content: "";
  position: absolute;
  top: 0;
  right: -5px;

  width: 7px;
  height: 7px;
  background-color: #000;
  border: 3px solid #fff;
  border-radius: 50%;
  z-index: 2;
}

/* 食物 */
.food {
  width: 20px;
  height: 20px;

  background-image: url(./img/mice.png);
  background-size: cover;
  position: absolute;
}

/* 开始按钮 */
.start {
  position: absolute;
  top: 50%;
  left: 50%;

  width: 200px;
  height: 100px;

  background-color: rgba(255, 255, 255, 0.692);
  transform: translate(-50%, -50%);
  text-align: center;
  line-height: 100px;
  display: none;
}

button {
  outline: none;
  padding: 5px 10px;

  border-radius: 10px;
  border: 2px solid #0063bd;
  background-image: linear-gradient(to bottom, #62ecfc, #029aea, #0061bc);

  color: #fff;
  font-size: 16px;
  font-weight: 600;
  box-shadow: 1px 2px 4px 2px rgba(0, 0, 0, 0.253);
}

button:active {
  background-image: linear-gradient(rgba(53, 53, 53, 0.199), rgb(53, 53, 53, 0.2));
}

/* 提示框 */
.tips_con {
  position: absolute;
  top: 4px;
  right: 4px;

  width: 22px;
  height: 22px;

  background-color: rgba(255, 255, 255, 0.704);
  border: 1px solid rgb(15, 15, 15);
  border-radius: 50%;

  text-align: center;
  line-height: 22px;
  color: #000;
  font-weight: bold;
}

.tips_con text {
  display: inline-block;
  width: 22px;
  height: 22px;
  border-radius: 50%;

  background-color: rgba(255, 255, 255, 0.704);
  text-align: center;
  line-height: 22px;
  color: #000;
  font-weight: bold;
}

.tips_con text:hover {
  background-color: #44bcd1;
  box-shadow: 0px 0px 0px 1px #44bacf;
}

.tips_con ul {
  position: absolute;
  top: -24px;
  left: 45px;

  margin: 0;
  padding: 0;

  border: 1px solid rgb(0, 0, 0);
  background-color: #fff;
  width: 200px;
  display: none;
}

.tips_con ul li {
  list-style-type: none;
  padding: 4px 0;
}

.tips_con text:hover+ul {
  display: block;
}

/* 底部分数 */
#infoBar {
  position: absolute;
  bottom: -30px;
  left: 50%;

  height: 28px;
  line-height: 28px;

  background-color: rgb(255, 234, 234);
  padding: 0px 30px;

  text-align: center;
  font-size: 17px;
  transform: translateX(-50%);
  border: 1px solid #fff;
}

/* 音效 */
audio {
  visibility: hidden;
  display: absolute;
}
最重要的 JavaScript
window.onload = function () {

  // 点击开始按钮
  var start = document.querySelector(".start");
  // 食物盒子
  var foods = document.querySelector(".foods");
  // 蛇盒子
  const Snake = document.querySelector(".snake");
  // 自动移动定时器ID
  var timer;
  // 分数
  const score = document.querySelector("#score");
  // 音效
  const audio = document.querySelector("audio");

  // 设置最大速度和最小速度常量
  const maxSpeed = 100, minSpeed = 140

  // 开始游戏
  startGame = function () {
    setTimeout(() => {
      // 隐藏开始按钮
      start.style.display = "none";
      // 初始蛇
      init();
      // 开始
      move(2);
      //  监听方向改变
      window.addEventListener("keydown", judgeDrec, false);
      window.addEventListener("keyup", speedDown, false)
    }, 200);
  };

  // 保存蛇头节点
  var snakeHead
  // 初始化蛇和食物
  init = function () {
    SnakeBodyList = [
      [490, 290],
      [490, 310],
      [490, 330],
    ];
    Snake.innerHTML = ` <div class="snake-node" id="snakeHead"></div>
    <div class="snake-node"></div>
    <div class="snake-node"></div>`;
    snakeHead = document.querySelector("#snakeHead")
    Snake.style.display = "block";
    addFood();
    addFood();
    addFood();
    addFood();
  };


  // 旋转头
  function rotateHead(dire) {
    // 1 : 左 ; 2:上; 3:右 ; 4 : 下
    switch (dire) {
      case 1: {
        snakeHead.style.transform = "translate(-50%,-50%) rotate(-90deg)";  // 注意先后顺序。否则会出错
        break
      }
      case 3: {
        snakeHead.style.transform = "translate(-50%,-50%) rotate(90deg) ";
        break
      }
      case 4: {
        snakeHead.style.transform = "translate(-50%,-50%) rotate(180deg) ";
        break
      }
      default: {
        snakeHead.style.transform = " translate(-50%,-50%) rotate(0deg)";
      }
    }
  }

  // 方向变量
  let dire = 2;
  // 设置节流阀
  let stop = 0;
  // 移动速度
  let speed = minSpeed;

  // 判断方法和同时判断是否加速
  function judgeDrec(e) {
    //  1 : 左 ; 2:上; 3:右 ; 4 : 下

    // 如果加速
    if (e.keyCode == 16) {
      // 并且速度小
      if (speed === minSpeed) {
        speed = maxSpeed;
        move(dire);
        stop = 0;
      }
      return
    }

    // 设置节流
    if (stop) {
      return;
    }
    stop = 1;
    setTimeout(() => {
      stop = 0;
    }, speed);
    // 判断方向
    switch (e.keyCode) {
      case 37: {
        if (dire === 3 || dire === 1) break;
        move(1);
        dire = 1;
        break;
      }
      case 38: {
        if (dire === 4 || dire === 2) break;
        move(2);
        dire = 2;
        break;
      }
      case 39: {
        if (dire === 1 || dire === 3) break;
        move(3);
        dire = 3;
        break;
      }
      case 40: {
        if (dire === 2 || dire === 4) break;
        move(4);
        dire = 4;
        break;
      }
    }
  }

  // 移动函数
  function move(direction) {
    // 旋转头部
    rotateHead(direction)

    // 清理上一次的定时器,免得影响移动方向
    clearInterval(timer);

    // 判断移动方向并且赋值移动距离
    const moveT = direction === 2 ? -20 : direction === 4 ? 20 : 0;
    const moveL = direction === 1 ? -20 : direction === 3 ? 20 : 0;

    // 设置定时器,定时移动
    timer = setInterval(() => {
      const left = SnakeBodyList[0][0] + moveL,
        top = SnakeBodyList[0][1] + moveT;

      // 判断是否死亡 ,没有死亡再进行移动
      if (!ifDeath(top, left))
        ifGrowth(top, left);
    }, speed);
  }

  // 减少速度
  function speedDown(e) {
    if (e.keyCode == 16) {
      speed = minSpeed
      move(dire);
    }
    return
  }

  // 判断是否死亡 , 返回布尔值 , true 表示死亡
  function ifDeath(top, left) {
    let end = false
    if (top >= 580 || top <= 0 || left <= 0 || left >= 980) {
      endGame();
      end = true
    }
    SnakeBodyList.some((v, i) => {
      if (i === 0) return false;
      if (v[0] === left && v[1] === top) {
        endGame();
        end = true
        return true;
      }
      return false;
    });
    return end
  }

  // 判断是否吃到食物
  ifGrowth = function (top, left) {
    // 检测蛇头是否于食物同位置
    let ifgrowth = foodPosition.some((v, i) => {
      if (v[0] === top - 10 && v[1] === left - 10) {
        foodPosition.splice(i, 1);
        return true;
      }
      return false;
    });
    if (ifgrowth) {
      // 播放音效
      audio.play();
      // 成长
      snakeGrowth([left, top], 1);
      // 增加食物
      addFood();
      // 删除吃掉的食物
      decreaseFood(top - 10, left - 10);
      return;
    }
    // 不成长
    snakeGrowth([left, top], 0);
  };

  // 食物随机生成
  var foodPosition = [];
  addFood = function () {
    //  left 0-960 - 20一个单位  -- 0-58的整数
    // top 0-560 - 20 一个单位   -- 0 -28 的整数

    // 生成随机位置
    const left = Math.round(Math.random() * 48) * 20;
    const top = Math.round(Math.random() * 28) * 20;
    // 判断会不会与旧食物位置重合
    let isadd = foodPosition.every((v) => {
      if (v[0] === top && v[1] === left) {
        addFood();
        return false;
      }
      return true;
    });
    if (isadd) {
      foodPosition.push([top, left]);
      const food = `<div class="food" id='food${top}${left}' style='left:${left}px;top:${top}px'></div>`;
      foods.innerHTML += food;
    }
  };

  // 删除吃掉的食物
  decreaseFood = function (top, left) {
    score.innerText = score.innerText / 1 + 10;
    const id = `food${top}${left}`;
    foods.removeChild(document.querySelector("#" + id));
  };

  // 蛇身体节点位置数组
  var SnakeBodyList = [
    [490, 290],
    [490, 310],
    [490, 330],
  ];

  // 蛇的移动和成长
  snakeGrowth = function (newPositipn, add) {
    // 增加蛇长度
    for (let i = 0; i < add; i++) {
      SnakeBodyList.push(SnakeBodyList[SnakeBodyList.length - 1]);
      let newNode = document.createElement("div")
      newNode.setAttribute("class", "snake-node")
      Snake.append(newNode)
      newNode = null
    }
    // 向位置坐标添加头位置,删除尾部位置
    if (newPositipn) {
      SnakeBodyList.unshift(newPositipn);
      SnakeBodyList.pop();
    }
    // 移动身体
    const SnakeList = document.querySelectorAll(".snake-node");
    for (let i = 0; i < SnakeBodyList.length; i++) {
      SnakeList[i].style.left = `${SnakeBodyList[i][0]}px`;
      SnakeList[i].style.top = `${SnakeBodyList[i][1]}px`;
      SnakeList[1].style.opacity = 1
    }
  };

  // 游戏终止
  function endGame() {
    // 停止移动
    clearInterval(timer);
    alert("游戏结束");
    window.removeEventListener("keydown", judgeDrec, false);
    // 恢复
    Snake.innerHTML = "";
    Snake.style.display = "none";

    // 回复原始方向
    dire = 2

    // 清空食物位置数组
    foodPosition = [];
    // 情况食物
    foods.innerHTML = "";
    // 清空得分
    score.innerText = 0;
    // 显示开始按钮
    start.style.display = "block";
  }
};

注意

  • 这里我设置了吃掉食物后的音效。大家可以去找一个音效的 URL放入即可。不需要可以删掉相关的代码避免报错
  • 蛇的眼睛是伪元素,不是图片,墙和老鼠的图片大家搜一搜换一下URL就可以了。

大家有什么不懂或者有什么高见欢迎评论或私聊。

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无糖的酸奶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值