首页 > 其他分享 >JS贪吃蛇

JS贪吃蛇

时间:2024-08-13 09:51:28浏览次数:7  
标签:direction 20 food 位置 draw JS 贪吃蛇 snake

1. 整个简洁版的贪吃蛇

  • 完整代码,后面会解析
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>贪吃蛇</title>
</head>
<body style="height: 90vh; display: flex; justify-content: center; align-items: center;">
  <canvas id="gameBox" width="400" height="400" style="background-color: black">对不起,您的浏览器不支持canvas</canvas>
  <script>
    var snake = [41, 40], //snake队列表示蛇身,初始节点存在但不显示
        direction = 1, //1表示向右,-1表示向左,20表示向下,-20表示向上
        food = 53, //食物的位置
        n, //与下次移动的位置有关
        box = document.getElementById('gameBox').getContext('2d');
    //seat从0到399表示box里[0~19]*[0~19]的所有节点,每20px一个节点
    function draw(seat, color) {//根据位置画色块
      box.fillStyle = color;
      box.fillRect(seat % 20 * 20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);
    }
    document.onkeydown = function (evt) {//监听方向
      direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
    };
    (function () {
      //添加新蛇头
      snake.unshift(n = snake[0] + direction);
      //碰撞检测
      if (snake.indexOf(n, 1) > 0 || n < 0 || n > 399 || direction == 1 && n % 20 == 0 || direction == -1 && n % 20 == 19) {
        return alert(`GAME OVER! Mark: ${snake.length - 1}`);
      }
      draw(n, "lime"); //画出蛇头下次出现的位置
      if (n == food) { //如果吃到食物时,产生一个蛇身以外的随机的点,不会去掉蛇尾
        if (snake.length == 400) return alert("NB!!!!"); //完美成功
        while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);
        draw(food, "yellow");
      } else { //没有吃到食物时正常移动,蛇尾出队列
        draw(snake.pop(), "black");
      }
      setTimeout(arguments.callee, 200);
      //每隔0.2秒执行函数一次,可以调节蛇的速度
    })(draw(food, "yellow"));
  </script>
</body>
</html>

image

2. 解析

2.1 变量

  • box = document.getElementById('gameBox').getContext('2d')画布对象,用于画图
    • 游戏地图是一个20X20的方格,这里将二维降为一维,以0--399标识每个方格
  • snake = [41, 40]存储蛇的位置节点,第0个节点是蛇头,初始有两个节点
  • direction = 1当前移动方向,1表示向右,-1表示向左,20表示向下,-20表示向上
  • food = 53当前食物位置
  • n与下次移动的位置有关,主要用于检测

2.2 画图方法

    //seat从0到399表示box里[0~19]*[0~19]的所有节点,每20px一个节点
    function draw(seat, color) {//根据位置画色块
      box.fillStyle = color;
      box.fillRect(seat % 20 * 20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);
    }
  • 蛇和食物都是方格,只要区分位置和颜色就行
  • seat表示一维的位置,需要转成二维的实际像素位置
  • seat % 20 * 20 + 1,x轴实际像素位置
    • 第一个20表示每行20个方格,%20取余获取在当前行第几个
    • 第二个20表示每个方格在x轴方向占20px,*20获取在x轴方向的实际像素位置
    • +1是方格间隔1px,用于视觉区分方格
  • ~~(seat / 20) * 20 + 1,y轴实际像素位置
    • ~~将一个数字取反两次,可以获取数字整数部分
    • 第一个20表示每行20个方格,~~(seat / 20)取整获取在当前第几行
    • 第二个20表示每个方格在y轴方向占20px,*20获取在y轴方向的实际像素位置
    • +1是方格间隔1px,用于视觉区分方格
  • 18, 18,因为上下左右都有1px的间隙,所以20X20变成了18X18

2.3 监听键盘

document.onkeydown = function (evt) {//监听方向
  direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
};
  • onkeydown,在页面上监听键盘按下事件
  • (evt || event).keyCode,兼容获取按下的按键的数字代码
    • 左上右下代码,('ArrowLeft',37) ('ArrowUp',38) ('ArrowRight',39) ('ArrowDown',40)
  • (evt || event).keyCode - 37,将数字码37,38,39,40转为0,1,2,3
  • [-1, -20, 1, 20][(evt || event).keyCode - 37],将按键码转为游戏方向,如果是其他按键,则结果为undefined
  • (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction),获取用户按键方向或者维持原方向,并赋值给n
  • snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction)
    简化:snake[1] - snake[0] == n,当前方向是否和上次方向一致
  • direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
    简化:direction = snake[1] - snake[0] == n ? direction : n,如果当前方向和上次方向不一致,则改变方向

2.4 主程序

(function () {
      //添加新蛇头
      snake.unshift(n = snake[0] + direction);
      //碰撞检测
      if (snake.indexOf(n, 1) > 0 || n < 0 || n > 399 || direction == 1 && n % 20 == 0 || direction == -1 && n % 20 == 19) {
        return alert(`GAME OVER! Mark: ${snake.length - 1}`);
      }
      draw(n, "lime"); //画出蛇头下次出现的位置
      if (n == food) { //如果吃到食物时,产生一个蛇身以外的随机的点,不会去掉蛇尾
        if (snake.length == 400) return alert("NB!!!!"); //完美成功
        while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);
        draw(food, "yellow");
      } else { //没有吃到食物时正常移动,蛇尾出队列
        draw(snake.pop(), "black");
      }
      setTimeout(arguments.callee, 200);
      //每隔0.2秒执行函数一次,可以调节蛇的速度
    })(draw(food, "yellow"));

2.4.1 开始执行

  • 主体简化:(function(){})(),自执行函数,定义一个无名函数并立刻执行
  • (function(){})(draw(food, "yellow")),执行函数时传了一个参数,其实就是在执行函数之前执行draw(food, "yellow")画食物

2.4.2 移动并检测

  • snake.unshift(n = snake[0] + direction);添加下一步移动的位置为蛇头
  • snake.indexOf(n, 1) > 0忽略第一个,检查后面有没有与蛇头位置一样的,即检查有没有撞身体
  • n < 0 || n > 399有没有上下越界
  • direction == 1 && n % 20 == 0向右但下一步的位置在最左边,即向右越界
  • direction == -1 && n % 20 == 19向左但下一步的位置在最右边,即向左越界
  • return alert(GAME OVER! Mark: ${snake.length - 1});如果下一步非法,结束并打印蛇的长度
  • draw(n, "lime");画出蛇头下一步出现的位置
  • n == food如果蛇头位置是食物位置,即吃到食物,不用移动蛇尾,即长度加1
  • if (snake.length == 400) return alert("NB!!!!");蛇的长度铺满地图,完美成功,结束游戏
  • while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);查找一个空位生成食物
  • draw(food, "yellow")画出食物
  • draw(snake.pop(), "black");如果蛇头位置不是食物位置,则移动蛇尾,即删除蛇尾,并将蛇尾位置画为地图颜色

2.4.3 循环执行

  • arguments这是函数内部的一个与当前函数有关的对象
  • arguments.callee可以引用函数本身
  • setTimeout(arguments.callee, 200)200毫秒后调用函数,可以当做移动速度

标签:direction,20,food,位置,draw,JS,贪吃蛇,snake
From: https://www.cnblogs.com/value410781/p/18351080

相关文章

  • Ubuntu22 安装和恢复 Hexo,解决nodejs默认版本较低的问题。
    因为Ubuntu自带的nodejs版本和我之前搭建好的hexo环境不符合,从github上面拉取之后不能够直接使用hexo配置,要重新下载和配置nodejs的版本。安装nvm安装步骤sudoaptinstallcurlcurlhttps://raw.githubusercontent.com/creationix/nvm/master/install.sh|bash出现问题......
  • JSON前后端传输数据
    一.简介JSON(JavaScriptObjectNotation,JS对象简谱)是一种轻量级的数据交换格式。它基于ECMAScript(EuropeanComputerManufacturersAssociation,欧洲计算机协会的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得JSON成为理想的数......
  • 基于springboot+vue.js+uniapp的宠物健康顾问系统附带文章源码部署视频讲解等
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaits系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于ssm+vue.js+uniapp的基于冲突动态监测算法的健身房预约系统附带文章和源代码部署
    文章目录前言详细视频演示具体实现截图技术栈后端框架SSM前端框架Vue持久层框架MyBaits系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 深入解析Node.js中的fs.watch:options与listener详解
    在Node.js中,fs.watch方法是一个功能强大的文件系统监控工具,它允许我们对文件或目录进行实时监控,并在文件或目录发生变化时触发相应的操作。在使用fs.watch时,两个关键的部分是options对象和listener回调函数。本文将详细讲解这两个部分,帮助读者更好地理解和使用fs.watch。一......
  • Js if 判断 for循环
    三大结构顺序结构:从上到下执行分支结构:选择性执行循环结构:重复执行什么是流程分支结构?条件控制(逻辑分支),就是根据我们设定好的条件来控制程序执行的方式,JavaScript提供了很多控制语法,目前我们先学习使用一种:if()else基本语法语法1-if语句单分支//基本语法:if......
  • JS中关于为什么调用构造函数要使用new的详细解读
    在JavaScript中,使用new关键字调用构造函数是创建新对象的关键步骤。本文将从以下几个方面解释为什么要这样做:1.创建一个新的对象当你用new调用构造函数时,会自动创建一个新的空对象,这个对象会被赋值给this,即构造函数内部的this关键字会引用这个新创建的对象。fu......
  • 鸿蒙-JS-第二周day01
    数组1什么是数组1)数组是值的有序集合。2)每个值叫做一个元素。3)每个元素在数组中有一个位置,以数字表示,称为索引(有时也称为下标)。4)数组的元素可以是任何类型。5)数组索引从0开始,数组最大能容纳4294967295个元素。2创建数组2.1使用数组直接量//......
  • ssm基于java web的商铺租赁管理系统的jsp管理系统|【源码+论文+PPT+部署视频】
    我们提供多元化的技术项目服务,涵盖Java、PHP、Python等编程语言,以及前端开发、人工智能、大数据、单片机开发、ASP.NET、物联网等领域。我们还提供简历模板、面试题库和学习资料,帮助用户提升技术能力和就业竞争力。我们的服务内容包括:免费功能设计、任务书和开题报告撰写、中......
  • ssm农产品预售平台的设计和实现jsp管理系统|【源码+论文+PPT+部署视频】
    我们提供多元化的技术项目服务,涵盖Java、PHP、Python等编程语言,以及前端开发、人工智能、大数据、单片机开发、ASP.NET、物联网等领域。我们还提供简历模板、面试题库和学习资料,帮助用户提升技术能力和就业竞争力。我们的服务内容包括:免费功能设计、任务书和开题报告撰写、中......