JS实现贪吃蛇

2023-10-29

JS实现贪吃蛇

1.结构

创建一个盒子box作为蛇的身体,当前盒子中只有一个子元素,代表此时蛇的长度为1.
在创建一个盒子food作为贪吃蛇的食物。

 <div id="box">
        <div></div>
 </div>
 <div id="food"></div>

2.CSS

设置蛇和食物的样式,这里注意蛇和食物都是绝对定位。

<style>
    *{
        padding: 0px;
        margin: 0px;
    }
    #box div{
        width: 30px;
        height: 30px;
        box-sizing: border-box;
        background: green;
        border: 1px solid black;
        position: absolute;
    }
    #food{
        width: 30px;
        height: 30px;
        background: brown;
        position: absolute;
    }
    </style>

3.脚本

获取蛇的身体和每一个子元素
var box = document.getElementById("box");
var boxs = document.getElementById("box").children;
定义蛇头的位置
 var snackX = 0;
 var snackY = 0;
获取屏幕宽度和高度,以此来设定墙的边界,以限制蛇的移动范围。
 var cw = document.documentElement.clientWidth;
 var ch = document.documentElement.clientHeight;
 var minsnackX = 0;
 var maxsnackX = Math.floor(cw / boxs[0].offsetWidth)*boxs[0].offsetWidth;
 var minsnackY = 0;
 var maxsnackY = Math.floor(ch / boxs[0].offsetHeight)*boxs[0].offsetHeight;
定义初始的移动方向。
  var turn = "right";
获取食物元素,并设置食物的位置坐标。
 var foodele = document.getElementById("food");
    var foodX,foodY;
蛇的初始化
  for(var i = 0; i <6 ; i++){
        box.appendChild(boxs[0].cloneNode(true));
    }
刷新食物
 function food(){
 	//此处的坐标要先获取页面最大支持的蛇身体的块数,然后在块数中随机,然后乘以块数的大小,
 	//因为蛇的移动每一步都是固定的,想要判定食物和蛇头重合就必须坐标是整块的倍数。
   	 foodX = parseInt( Math.random()*Math.floor(cw / boxs[0].offsetWidth))*boxs[0].offsetWidth;
     foodY = parseInt( Math.random()*Math.floor(ch / boxs[0].offsetHeight))*boxs[0].offsetHeight;
        //判定当食物的产生位置和蛇的任何一个位置重合时就重新生成食物。
        for(var i = 0;i<boxs.length;i++){
            if(foodX + "px" === boxs[i].style.left && foodY + "px" === boxs[i].style.top){
                food();
            }
        }
        foodele.style.left = foodX + "px";
        foodele.style.top = foodY + "px";
 }
调用food()方法 生成第一个食物
   food();
设置定时器 每次执行一次蛇的运行方法
   var timer = setInterval(function(){
        snackMOve();
    },150)
封装一个蛇的运动方法
  //移动和判定边界
    function snackMOve(){
    //此处为判定方向 根据判定的方向,向改方向前进一个方块
        switch(turn){
            case "right":snackX +=30;break;
            case "left":snackX -=30;break;
            case "bottom":snackY +=30;break;
            case "top":snackY -=30;break;
        }
        //如果蛇越过了墙就从另一端出现
        if(snackX > maxsnackX){
            snackX = 0;
        }
        if(snackX < minsnackX){
            snackX = maxsnackX;
        }
        if(snackY > maxsnackY){
            snackY = 0;
        } 
        if(snackY < minsnackY){
            snackY = maxsnackY;
        }
        //从最后一个开始,每个元素跟随上一个元素的位置
        for(var i = boxs.length-1; i >0 ; i--){
            boxs[i].style.left = boxs[i-1].style.left;
            boxs[i].style.top =  boxs[i-1].style.top ;
        }
        //第一个也就是蛇头的位置,永远是根据方向获取的位置
        boxs[0].style.left = snackX + "px";
        boxs[0].style.top =  snackY + "px" ;


        //判定吃到食物 就长大和刷新
        //当蛇头位置移动之后与食物重合 那么刷新食物,并且在蛇的身体中插入一个克隆的元素,相当于长度+1
        if(snackX  === foodX && snackY  === foodY){
            food();
            box.appendChild(boxs[0].cloneNode(true));
        }else{
        //判定撞死 
        //当蛇头与身体中的任何一个元素重合,那么判定结束游戏,停止定时器
            for(var i = 1;i<boxs.length;i++){
                if(snackX + "px" === boxs[i].style.left && snackY + "px" === boxs[i].style.top){
                    clearInterval(timer);
                    alert("失败");
                }
            }
        }
    }

蛇的运动方向

document.onkeydown = function(eve){
    var e = eve||event;
    var keyCode = e.keyCode||e.which;
    switch(keyCode){
        case 37:if(turn === "right"){break;}turn = "left";break;
        case 38:if(turn === "bottom"){break;}turn = "top";break;
        case 39:if(turn === "left"){break;}turn = "right";break;
        case 40:if(turn === "top"){break;}turn = "bottom";break;
    }        
}

全部代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
    *{
        padding: 0px;
        margin: 0px;
    }
    #box div{
        width: 30px;
        height: 30px;
        box-sizing: border-box;
        background: green;
        border: 1px solid black;
        position: absolute;
    }
    #food{
        width: 30px;
        height: 30px;
        background: brown;
        position: absolute;
    }
    </style>
</head>
<body>
    <div id="box">
        <div></div>
    </div>
    <div id="food"></div>
    <script>
        var box = document.getElementById("box");
        var boxs = document.getElementById("box").children;
        var snackX = 0;
        var snackY = 0;
        var cw = document.documentElement.clientWidth;
        var ch = document.documentElement.clientHeight;
        var minsnackX = 0;
        var maxsnackX = Math.floor(cw / boxs[0].offsetWidth)*boxs[0].offsetWidth;
        var minsnackY = 0;
        var maxsnackY = Math.floor(ch / boxs[0].offsetHeight)*boxs[0].offsetHeight;
        var turn = "right";
        var foodele = document.getElementById("food");
        var foodX,foodY;

        for(var i = 0; i <6 ; i++){
            box.appendChild(boxs[0].cloneNode(true));
        }


        //随机食物
        function food(){
            foodX = parseInt( Math.random()*Math.floor(cw / boxs[0].offsetWidth))*boxs[0].offsetWidth;
            foodY = parseInt( Math.random()*Math.floor(ch / boxs[0].offsetHeight))*boxs[0].offsetHeight;
            for(var i = 0;i<boxs.length;i++){
                if(foodX + "px" === boxs[i].style.left && foodY + "px" === boxs[i].style.top){
                    food();
                }
            }
            foodele.style.left = foodX + "px";
            foodele.style.top = foodY + "px";
        }
        food();

        //设置定时器 移动
        var timer = setInterval(function(){
            snackMOve();
        },150)


        //移动和判定边界
        function snackMOve(){
            switch(turn){
                case "right":snackX +=30;break;
                case "left":snackX -=30;break;
                case "bottom":snackY +=30;break;
                case "top":snackY -=30;break;
            }
            //根据边界归零
            if(snackX > maxsnackX){
                snackX = 0;
            }
            if(snackX < minsnackX){
                snackX = maxsnackX;
            }
            if(snackY > maxsnackY){
                snackY = 0;
            } 
            if(snackY < minsnackY){
                snackY = maxsnackY;
            }
            for(var i = boxs.length-1; i >0 ; i--){
                boxs[i].style.left = boxs[i-1].style.left;
                boxs[i].style.top =  boxs[i-1].style.top ;
            }
            boxs[0].style.left = snackX + "px";
            boxs[0].style.top =  snackY + "px" ;


            //判定吃到食物 就长大和刷新
            if(snackX  === foodX && snackY  === foodY){
                food();
                box.appendChild(boxs[0].cloneNode(true));
            }else{
            //判定撞死 暂停计时器 刷新
                for(var i = 1;i<boxs.length;i++){
                    // console.log(boxs[i].style.left);
                    if(snackX + "px" === boxs[i].style.left && snackY + "px" === boxs[i].style.top){
                        clearInterval(timer);
                        alert("失败");
                        // console.log(1)
                    }
                }
            }
        }



        //方向
        document.onkeydown = function(eve){
            var e = eve||event;
            var keyCode = e.keyCode||e.which;
            switch(keyCode){
                case 37:if(turn === "right"){break;}turn = "left";break;
                case 38:if(turn === "bottom"){break;}turn = "top";break;
                case 39:if(turn === "left"){break;}turn = "right";break;
                case 40:if(turn === "top"){break;}turn = "bottom";break;
            }        
        }
    </script>
</body>
</html>

总结

贪吃蛇的思路主要是有以下几个部分
1.食物的随机出现(不能随机在蛇身上)
2.定时器控制蛇的移动
3.墙的判定
4.蛇的运动逻辑
5.运动方向的判定
6.吃到食物的判定
7.蛇头与身体的判定(即游戏结束的判定)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JS实现贪吃蛇 的相关文章

  • 在 HTML5 Javascript 中将 BlobBuilder 转换为字符串

    function blobToString blob var reader new FileReader var d reader onloadend function d callback reader result console lo
  • 无法使用 BeautifulSoup4 (Python 3) 抓取特定表

    我想从 Ligue 1 足球网站上抓取一张表格 具体来说 该表包含有关卡片和裁判的信息 http www ligue1 com LFPStats stats arbitre competition D1 http www ligue1 co
  • 网站在 iPhone 屏幕右侧显示空白区域

    我遇到问题http eiglaw com http eiglaw com iPhone 屏幕右侧显示约 25 像素宽的空白 边框 我在 stackoverflow 上研究了这个问题 这些帖子是相关的 但是当我尝试提供的各种解决方案时 我无法
  • 将 XSL-FO 转换为 HTML

    我有一组用于 PDF 生成的 XSL FO 文档 我还需要将相同的输出数据 PDF 格式 导出为 HTML 文件 此外 我需要 HTML 具有与 PDF 类似的样式 有没有办法使用 C 将 XSL FO 转换为 XHTML NOTE 我知道
  • 滚动时的 CSS 背景模糊

    我有固定的背景图像 滚动时我希望图像变得模糊 我知道如何在 css 中进行模糊 但在特定的滚动位置进行 这是一个例子 https medium com good music f160ba9e6c52 https medium com goo
  • 导航栏下拉菜单(折叠)在 Bootstrap 5 中不起作用

    我在尝试使用以下命令创建响应式菜单或下拉按钮时遇到问题Bootstrap 5一切似乎都正常 导航图标和下拉图标出现 但它不起作用 当我单击nav图标或dropdown按钮 无dropdown menu apears 我想特别提到的是 我还包
  • 如何使用角度材料在具有可扩展行的表格中创建嵌套垫表

    我有以下数据 id c9d5ab1a subdomain wing domain aircraft part id c9d5ab1a info mimetype application json info dependent parent
  • 使用 Javascript 设置 cookie [重复]

    这个问题在这里已经有答案了 我正在尝试构建我的第一个移动应用程序 它需要连接到我的 mysql 数据库并使用 json 返回数据 这很好 目前我有一个登录系统 一旦确定用户名和密码存在 它就会返回一条成功消息 对于下一步 我想在我的页面上使
  • 禁用允许文本选择的

    残疾人可以吗
  • 图像随机损坏(但刷新后加载)并显示“资源解释为图像但使用 MIME 类型 text/html 传输”

    我目前正在开发一个简单的 php 网站 问题是 我的整个网站中的图像 发生在所有 php 文件中 随机损坏并显示错误资源解释为图像 但以 MIME 类型 text html 传输但是 如果我尝试多次刷新页面 可以再次加载图像并且错误消失 我
  • 无法读取未定义的属性“搜索”

    我正在尝试制作一个使用 YouTube API 的脚本 我输入了一个关键字 youtube api 找到视频 gt 脚本获取第一个结果并返回 VideoID 现在我的问题是 当我按下提交按钮时 搜索功能不会被触发 有谁知道这可能是什么原因
  • IE 11 中的 CSS Display Flex 无法正常工作,图像会溢出

    我正在使用 CSSdisplay flex 以某种方式显示它们 在 Chrome 中 它看起来很完美 很漂亮 但在 IE 中 图像超出了我放置它们的元素 https jsfiddle net m42k7Lk5 8 https jsfiddl
  • Chrome:当父级固定时,无法将一个绝对 div 放置在另一个 div 上

    我发现 当我想要位于顶部的 div 的父级固定时 我无法将一个绝对定位的 div 放置在 Chrome 中的另一个 div 上 div div div div div div 这是演示该问题的 JSFiddle http jsfiddle
  • 更改元素的顺序

    我正在创建一个浮动宽度的网站 用户在智能手机上使用从全高清分辨率到约 600 像素的屏幕 这似乎是一个不错的主意 这就带来了一个非常有趣的问题 当用户使用比最佳分辨率更小的分辨率时 页面的高度会增加很多 这意味着更改某些元素 例如某些图像
  • 如何在 select2 下拉列表中换行?

    我正在使用 select 2 下拉菜单 然后在其内容中显示一些长句子 我想在句子的正确位置添加换行符 但下拉菜单是自动调整的 For example the content of the dropdown right now looks l
  • @media语法/可能的组合

    我见过其中一些 media print media screen handheld print projection media all media all and property value media screen and prope
  • 如何在codeigniter中将上传图片比例限制为16:9?

    这是我用来上传图像的代码 this gt load gt library upload ext pathinfo file name PATHINFO EXTENSION img name now ext imgConfig upload
  • php - 解析html页面

    div divbox div p para1 p p para2 p p para3 p table class table tr td td tr table p para4 p p para5 p 有人可以告诉我如何解析这个 html
  • 如何创建适合屏幕宽度的等宽/高框? [复制]

    这个问题在这里已经有答案了 我正在尝试建立一个网站 其中有很多宽度和高度相等的框 例如 我有一个页面 其中并排有两个相同大小的框 简单的解决方案是将宽度和高度设置为 50vw 这在出现滚动条之前效果很好 我已经用谷歌搜索了几个小时 但无法理
  • 表格行未扩展到全宽

    我有一个表格 当我将表格的宽度设置为 100 并将表格行的宽度设置为 100 时 没有任何反应或宽度发生变化 Table Normal position relative display block margin 10px auto pad

随机推荐

  • 浏览器窗口间的通信

    一 汇总 二 同源策略 三 webSocket 无跨域限制 优点 无跨域限制 缺点 成本高 四 客户端存储 1 localStorage onStorage 例子 2 定时器 客户端存储 例子 缺点 五 postMessage 无跨域限制
  • RK3399交叉编译问题

    问题描述 老子拷贝同事的代码和RK3399文件包 妈md 出这个问题 解决 1 ls l usr lib aarch64 linux gnu libdl so 2 2 sudo rm usr lib aarch64 linux gnu li
  • 华为交换机在vlanif下绑定IP地址和MAC

    首先 我是在vlanif下启用dhcp的 2 1F Core1 Vlanif2 display this interface Vlanif2 ip address 172 16 15 1 255 255 254 0 dhcp select
  • 鸿蒙os和ios区别,华为鸿蒙OS和iOS以及安卓的区别到底在哪,一张图总结

    华为鸿蒙OS从2019年就在说了 很多人不了解什么是鸿蒙 也根本不知道这个OS和其它产品有什么区别 特别是未来华为想靠鸿蒙OS做什么 为什么华为愿意开源 为什么华为不像苹果一样做一个封闭的生态让自家产品的体验更好 今天借着下面这张图跟大家聊
  • 算法:跳跃游戏(js)

    题目 力扣 思路 试想一下 若从第三位置可以跳到第五位置 那只要前面有一个点能跳到第三位置即可说明整体能跳到第五位置 终点为最后一个点开始 看终点的前面是否存在一个点能跳到终点 若有这个点 此时终点为这个点 继续同样逻辑 代码 var ca
  • unsigned char和signed char型变量学习

    首先考虑下面代码输出什么 cpp view plain copy 1 include
  • Nginx 使用 HTTPS(准备证书和私钥)

    文章目录 Nginx生成自签名证书和配置Nginx HTTPS 准备证书和私钥 准备证书和私钥 Nginx生成自签名证书和配置Nginx HTTPS 准备证书和私钥 准备证书和私钥 生成私钥 openssl genrsa des3 out
  • 网络RJ45接口详解

    RJ45 简介 图 1 RJ45模块 RJ45模块用于实现PHY之间的互连 包括PHY芯片经信号变压器与RJ45接口相连 如图 1所示 RJ45连接器由插头和插座组成 RJ45插头又称水晶头 如图 3 10所示 这两种元件组成的连接器连接于
  • mmdetection训练数据遇到的问题

    1 Permission denied bash compile sh Permission denied 没有操作权限 改为赋予最高权限 777 chmod 777 compile sh 2 cuda问题 unable to execut
  • 无锁同步-C++11之Atomic和CAS

    首页 联系 订阅 无锁同步 C 11之Atomic和CAS 1 概要 本文是无锁同步系列文章的第一篇 主要探讨C 11中的Atomic 我们知道在C 11中引入了mutex和方便优雅的lock guard 但是有时候我们想要的是性能更高的无
  • 合并单元格

    protected void gridView PreRender object sender EventArgs e MergeRows gridView 合並單元格 public static void MergeRows GridVi
  • QT 子线程 更改UI

    一 问题原因 QT和Android类似 不能在子线程中直接更新UI 否则会有崩溃 二 实现方式 方式有几种 我这里只说一种 使用 信号机制 三 原理 原理其实是信号槽机制 槽连接方式 有以下四种 如果未设置默认 auto 即可跨线程通信 D
  • 蓝桥杯单片机(十三)AT24C02(EEPROM)

    AT24C02是一个2K位串行CMOS E2PROM 内部含有256个8位字节 CATALYST公司的先进CMOS技术实质上减少了器件的功耗 AT24C02有一个16字节页写缓冲器 该器件通过IIC总线接口进行操作 有一个专门的写保护功能
  • TL431应用电路与LTspice仿真

    TL431应用电路与LTspice仿真 文章目录 TL431应用电路 简介 应用电路1 稳压源 应用电路2 电压比较器 应用电路3 隔离型反馈电路 LTspice仿真 模型导入和使用 spice模型 原理图符号 放置模型 暂态仿真 DC扫描
  • 最大公约数,最小公倍数,素数等问题

    1 两个数的 最小公倍数 等于两个数的乘积除以最大公约数 scm a b a b gcd a b 所以主要是最大公约数问题 gcd 问题 辗转相除法 依据就是欧几里得定理 gcd a b gcd b a b def gcd a b whil
  • Docker三剑客——Compose

    前面介绍了Docker三剑客中的两个 今天我们介绍一下三剑客中的最后一个 docker compose 接下来的内容 我们还是从五个方面来讲解 主要包括如下 Compose简介 Compose安装与卸载 Compose常用命令 Compos
  • LinearEyeDepth 推导过程

    转载自 冯乐乐的 Unity Shader 入门精要 获取深度和法线纹理 虽然在Unity里获取深度和法线纹理的代码非常简单 但是我们有必要在这之前首先了解它们背后的实现原理 深度纹理实际上就是一张渲染纹理 只不过它里面存储的像素值不是颜色
  • 谈谈前华为荣耀软件测试工程师校招面试(已拿到offer)

    截止到现在 一共参加了2次笔试 2次面试 具体时间参照截图 机试 一共三道编程题 共500分好像 全对了一道 就是提交后通过测试 另外两道写完了 但是提交测试没通过 性格测试 就正常选了 保证前后一致啊 因为前面问到的后面还会有 不然会提示
  • 英语学习频道

    1 美国在线 www aol com 2 美国白宫 www whitehouse gov 3 路透网 中文 http cn reuters com 英文 http www reuters com 4 环球网 中文 http www huan
  • JS实现贪吃蛇

    JS实现贪吃蛇 1 结构 创建一个盒子box作为蛇的身体 当前盒子中只有一个子元素 代表此时蛇的长度为1 在创建一个盒子food作为贪吃蛇的食物 div div div div div div 2 CSS 设置蛇和食物的样式 这里注意蛇和食