20行js代码实现的贪吃蛇小游戏


Posted in Javascript onJune 20, 2017

前言

最近在csdn上看到一位大神用20行代码就写出了一个贪吃蛇的小游戏,感觉被惊艳到了,就试着读了一下这段代码,阅读过程中不断为作者写法的巧妙而叫绝,其中我发现自己对运算符优先级和一些js的技巧不是很清楚,所以看完之后决定把思路分享出来,方便和我一样的小白学习。

我对代码稍稍做了些修改,并添加了一些注释,方便理解。

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>贪吃蛇重构</title>
 <style>
  body {
   display: flex;
   height: 100vh;
   margin: 0;
   padding: 0;
   justify-content: center;
   align-items: center;
  }
 </style>
</head>
<body>
 <canvas id="can" width="400" height="400" style="background-color: black">对不起,您的浏览器不支持canvas</canvas>
 <script>
 
  var snake = [41, 40],  //snake队列表示蛇身,初始节点存在但不显示
   direction = 1,   //1表示向右,-1表示向左,20表示向下,-20表示向上
   food = 43,    //食物的位置
   n,      //与下次移动的位置有关
   box = document.getElementById('can').getContext('2d');
         //从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);
         //用color填充一个矩形,以前两个参数为x,y坐标,后两个参数为宽和高。
  }
  document.onkeydown = function(evt) { 
         //当键盘上下左右键摁下的时候改变direction
   direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
  };
  !function() {
   snake.unshift(n = snake[0] + direction); 
         //此时的n为下次蛇头出现的位置,n进入队列
   if(snake.indexOf(n, 1) > 0 || n < 0 || n > 399 || direction == 1 && n % 20 == 0 || direction == -1 && n % 20 == 19) {
         //if语句判断贪吃蛇是否撞到自己或者墙壁,碰到时返回,结束程序
    return alert("GAME OVER!");
   }
   draw(n, "lime");  //画出蛇头下次出现的位置
   if(n == food) {   //如果吃到食物时,产生一个蛇身以外的随机的点,不会去掉蛇尾
    while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);
    draw(food, "yellow");
   } else {    //没有吃到食物时正常移动,蛇尾出队列
    draw(snake.pop(),"black");
   }
   setTimeout(arguments.callee, 150);  
         //每隔0.15秒执行函数一次,可以调节蛇的速度
  }();
 </script>
</body>
</html>

首先,我们要知道做一个贪吃蛇最主要的是什么,是做出蛇活动的场所和如何使蛇动起来。

我们先看蛇活动的场所:

<!-- html -->
<canvas id="can" width="400" height="400" style="background-color: black">
 对不起,您的浏览器不支持canvas
</canvas>
<!-- js -->
box = document.getElementById('can').getContext('2d');

这是一个400px*400px的canvas,思路是以20px*20px为一个方格,组成20行20列的方阵,总共400格,然后绿色填充的格子表示蛇身,用黄色表示食物。这400个格子和数字0~399一一对应,对应的方式就是以20作为基数,n / 20再取整表示第几行,n % 20表示第几列。行数和列数都用0~19表示。

蛇用一个一维数组表示,每个值都是这400个数中的一个,用var snake = [41, 40];初始化这条蛇,索引0为蛇头。food表示食物的位置,direction表示蛇头下一次运动的转向。蛇的运动就用添加和删除数组元素来实现,每次执行绘制蛇头,去掉蛇尾,循环执行使蛇运动。

下边从函数运行的起始处(39行)开始看:

!function() {}();

什么鬼?这其实是立即执行函数IIFE的另一种写法。关于IIFE,这篇文章讲的挺不错的。继续往下看,给蛇头添加一个节点n,其值为当前蛇头的值加direction的值,如此一来就能理解为什么要用20表示向下,-20表示向上了。再下一行是一个if语句,其中值得提醒的是&&的优先级高于||,这个语句就是判断即将出现的蛇头是不是属于蛇身,或者跑到box外边去了。如果没有死亡,就把这个蛇头绘制出来,下边就看看绘制的代码:

function draw(seat, color) {
 box.fillStyle = color;
 box.fillRect(seat % 20 *20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);
}

填充时填充18*18的像素,留1px边框。 .fillRect()中第一个参数就是要绘制的矩形的x坐标seat % 20 *20 + 1,即先得到所要绘制的矩形块在方阵中的位置:第~~(seat / 20)行,第seat % 20列,再* 20 + 1具体到像素点。可能这个~~有点难理解,我感觉在这里的用处应该和Math.floor()差不多,对一个浮点型的数取反再取反,得到的数就是去掉小数位的整数了。

回到47行,又是一个判断语句,判断下次蛇头出现的位置是不是和当前的食物的位置相同,如果相同,生成下一个食物,食物的位置为一个随机数,但是要判断这个点不是出现在当前的蛇身上,绘制食物。如果没有吃到食物,即蛇在正常运动时,每向前一次,将蛇尾弹出,并利用其返回值将这个点重新绘制为黑色。

最后的setTimeout,循环执行当前函数,设置执行周期来调蛇的移动速度。

到了这里,我们发现这条蛇已经可以动了,加上键盘的操作就完成了:

document.onkeydown = function(evt) { 
 direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
};

将这个函数绑定到键盘事件上,evt || event用法的原因这里有详细的解释,是为了兼容ie。

三目运算符?前边的判断语句又可分为两部分:

  1. snake[1] - snake[0]的值应该就是-direction,按理说此处写成-direction应该和原来是一个效果,那为什么没有这么做呢,因为如果这样写,玩家可能在一个函数周期中多次改变direction的值,最后使得direction和当前真正的运动方向不一致,导致游戏崩溃。
  2. 在==后边, [-1, -20, 1, 20][(evt || event).keyCode - 37]中前边的[]是一个数组,后边的[]是取索引,左上右下四个键的keyCode分别为37, 38, 39, 40,计算后的索引为0, 1, 2, 3,使方向键与direction的取值对应起来。这里的巧妙之处在于如果按下的按键不是方向键,在数组中将得不到对应的值,返回undefine。此时,由于之后的||运算符,n会取到direction原来的值。

再用三目运算符来判断,如果按键方向不是反方向,就更新direction的值。

以上就是本篇的全部内容啦,虽然都是一些基础的东西,但是感觉还是挺好玩的。要是哪里理解的不对还希望指证出来,共同进步。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JavaScript实现班级随机点名小应用需求的具体分析
May 12 Javascript
javascript设置连续两次点击按钮时间间隔的方法
Oct 28 Javascript
JavaScript中操作Mysql数据库实例
Apr 02 Javascript
全面解析JS字符串和正则表达式中的match、replace、exec等函数
Jul 01 Javascript
JavaScript浮点数及运算精度调整详解
Oct 21 Javascript
node.js版本管理工具n无效的原理和解决方法
Nov 24 Javascript
浅谈js算法和流程控制
Dec 29 Javascript
jQuery基本选择器和层次选择器学习使用
Feb 27 Javascript
详解vue-router数据加载与缓存使用总结
Oct 29 Javascript
Vue 前端实现登陆拦截及axios 拦截器的使用
Jul 17 Javascript
vue微信分享插件使用方法详解
Feb 18 Javascript
vue-cli 3如何使用vue-bootstrap-datetimepicker日期插件
Feb 20 Vue.js
详解vue-resource promise兼容性问题
Jun 20 #Javascript
ionic2自定义cordova插件开发以及使用(Android)
Jun 19 #Javascript
详解vue2.0 transition 多个元素嵌套使用过渡
Jun 19 #Javascript
vue中如何实现变量和字符串拼接
Jun 19 #Javascript
js实现随机数字字母验证码
Jun 19 #Javascript
解决vue-cli中stylus无法使用的问题方法
Jun 19 #Javascript
AngularJS  ng-repeat遍历输出的用法
Jun 19 #Javascript
You might like
php mssql 数据库分页SQL语句
2008/12/16 PHP
PHP实现仿百度文库,豆丁在线文档效果(word,excel,ppt转flash)
2016/03/10 PHP
php读取本地json文件的实例
2018/03/07 PHP
CSS JavaScript 实现菜单功能 改进版
2008/12/09 Javascript
javascript 程序库的比较(一)之DOM功能
2010/04/07 Javascript
两个listbox实现选项的添加删除和搜索
2013/03/01 Javascript
jQuery计算textarea中文字数(剩余个数)的小程序
2013/11/28 Javascript
jQuery简单实现banner图片切换
2014/01/02 Javascript
JavaScript清空数组元素的两种方法简单比较
2015/07/10 Javascript
JS组件Bootstrap按钮组与下拉按钮详解
2016/05/10 Javascript
第七篇Bootstrap表单布局实例代码详解(三种表单布局)
2016/06/21 Javascript
JS继承之借用构造函数继承和组合继承
2016/09/07 Javascript
Javascript中call,apply,bind方法的详解与总结
2016/12/12 Javascript
微信小程序中hidden不生效原因的解决办法
2017/04/26 Javascript
JavaScript重复元素处理方法分析【统计个数、计算、去重复等】
2017/12/14 Javascript
在vue中获取微信支付code及code被占用问题的解决方法
2019/04/16 Javascript
JavaScript实现简单贪吃蛇效果
2020/03/09 Javascript
如何在面试中手写出javascript节流和防抖函数
2020/10/22 Javascript
浅析Python基础-流程控制
2016/03/18 Python
Python使用django框架实现多人在线匿名聊天的小程序
2017/11/29 Python
Python并行分布式框架Celery详解
2018/10/15 Python
Python多线程获取返回值代码实例
2020/02/17 Python
python实现测试工具(一)——命令行发送get请求
2020/10/19 Python
美国花布包包品牌:Vera Bradley
2017/08/11 全球购物
流行文化收藏品:Sideshow(DC漫画,星球大战,漫威)
2019/03/17 全球购物
美国厨房和园艺工具网上商店:Nestneed
2019/08/24 全球购物
Kiwi.com中国:找到特价机票并发现新目的地
2019/10/27 全球购物
工商企业管理实习自我鉴定
2013/12/04 职场文书
报告会主持词
2014/04/02 职场文书
服装设计专业自荐信
2014/06/17 职场文书
2015年会计人员工作总结
2015/05/22 职场文书
小学音乐课教学反思
2016/02/18 职场文书
八年级地理课件资料及考点知识分享
2019/08/30 职场文书
解决SpringCloud Feign传对象参数调用失败的问题
2021/06/23 Java/Android
分享7个 Python 实战项目练习
2022/03/03 Python
Windows Server 2008 修改远程登录端口以及配置防火墙
2022/04/28 Servers