技术文章 | jswz

web前端开发:20行代码的贪吃蛇

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

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


  1. <!DOCTYPE html>

  2. <html lang="en">

  3. <head>

  4.    <meta charset="UTF-8">

  5.    <title>贪吃蛇重构</title>

  6.    <style>

  7.        body {

  8.            display: flex;

  9.            height: 100vh;

  10.            margin: 0;

  11.            padding: 0;

  12.            justify-content: center;

  13.            align-items: center;

  14.        }

  15.    </style>

  16. </head>

  17. <body>

  18.    <canvas id="can" width="400" height="400" style="background-color: black">对不起,您的浏览器不支持canvas</canvas>

  19.    <script>

  20.        var snake = [41, 40],       //snake队列表示蛇身,初始节点存在但不显示

  21.            direction = 1,          //1表示向右,-1表示向左,20表示向下,-20表示向上

  22.            food = 43,              //食物的位置

  23.            n,                      //与下次移动的位置有关

  24.            box = document.getElementById('can').getContext('2d');

  25.                                    //从0到399表示box里[0~19]*[0~19]的所有节点,每20px一个节点

  26.  

  27.        function draw(seat, color) {

  28.            box.fillStyle = color;

  29.            box.fillRect(seat % 20 *20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);

  30.                                    //用color填充一个矩形,以前两个参数为x,y坐标,后两个参数为宽和高。

  31.        }

  32.  

  33.        document.onkeydown = function(evt) {    

  34.                                    //当键盘上下左右键摁下的时候改变direction

  35.            direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;

  36.        };

  37.  

  38.        !function() {

  39.            snake.unshift(n = snake[0] + direction);    

  40.                                    //此时的n为下次蛇头出现的位置,n进入队列

  41.            if(snake.indexOf(n, 1) > 0 || n < 0 || n > 399 || direction == 1 && n % 20 == 0 || direction == -1 && n % 20 == 19) {

  42.                                    //if语句判断贪吃蛇是否撞到自己或者墙壁,碰到时返回,结束程序

  43.                return alert("GAME OVER!");

  44.            }

  45.            draw(n, "lime");        //画出蛇头下次出现的位置

  46.            if(n == food) {         //如果吃到食物时,产生一个蛇身以外的随机的点,不会去掉蛇尾

  47.                while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);

  48.                draw(food, "yellow");

  49.            } else {                //没有吃到食物时正常移动,蛇尾出队列

  50.                draw(snake.pop(),"black");

  51.            }

  52.            setTimeout(arguments.callee, 150);      

  53.                                    //每隔0.15秒执行函数一次,可以调节蛇的速度

  54.        }();

  55.    </script>

  56. </body>

  57. </html>

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


  1.    <!-- html -->

  2.    <canvas id="can" width="400" height="400" style="background-color: black">

  3.        对不起,您的浏览器不支持canvas

  4.    </canvas>

  5.    <!-- js -->

  6.    box = document.getElementById('can').getContext('2d');

  这是一个 400px*400pxcanvas,思路是以 20px*20px为一个方格,组成 2020列的方阵,总共 400格,然后绿色填充的格子表示蛇身,用黄色表示食物。这 400个格子和数字 0~399一一对应,对应的方式就是以 20作为基数, n/20再取整表示第几行, n%20表示第几列。行数和列数都用 0~19表示。
  蛇用一个一维数组表示,每个值都是这 400个数中的一个,用 varsnake=[41,40];初始化这条蛇,索引 0为蛇头。 food表示食物的位置, direction表示蛇头下一次运动的转向。蛇的运动就用添加和删除数组元素来实现,每次执行绘制蛇头,去掉蛇尾,循环执行使蛇运动。
  下边从函数运行的起始处( 39行)开始看:


  1. !function() {}();

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


  1. function draw(seat, color) {

  2.    box.fillStyle = color;

  3.    box.fillRect(seat % 20 *20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);

  4. }

  填充时填充 18*18的像素,留 1px边框。 .fillRect()中第一个参数就是要绘制的矩形的 x坐标 seat%20*20+1,即先得到所要绘制的矩形块在方阵中的位置:第 ~~(seat/20)行,第 seat%20列,再 *20+1具体到像素点。可能这个 ~~有点难理解,我感觉在这里的用处应该和 Math.floor()差不多,对一个浮点型的数取反再取反,得到的数就是去掉小数位的整数了。
  回到 47行,又是一个判断语句,判断下次蛇头出现的位置是不是和当前的食物的位置相同,如果相同,生成下一个食物,食物的位置为一个随机数,但是要判断这个点不是出现在当前的蛇身上,绘制食物。如果没有吃到食物,即蛇在正常运动时,每向前一次,将蛇尾弹出,并利用其返回值将这个点重新绘制为黑色。
  最后的 setTimeout,循环执行当前函数,设置执行周期来调蛇的移动速度。
  到了这里,我们发现这条蛇已经可以动了,加上键盘的操作就完成了:


  1. document.onkeydown = function(evt) {    

  2.    direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;

  3. };

  将这个函数绑定到键盘事件上, 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的值。

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


Web前端发展前景如何? 请访问www.icketang.com/jswz/465.html
 



爱创课堂是一家专门做前端培训的机构,由百度高级工程师,《JavaScript设计模式》作者张容铭老师亲自授课,全程20个项目实战,真正做到学习完即可到企业正常工作!

我们前端课程的五大优势:

1. 名师亲自授课,课程无缝隙对接企业
2. 免费学习一周,不满意不收取任何费用
3. 签订就业协议,目前学员平均就业薪资12K
4. 采取小班教学模式(一个班20—30人)
5. 与中国石油,中国电信,软通等知名企业合作,进行企业内训,并进行学员就业推荐

 

关注微信公众号「爱创课堂网络科技」

获取前端相关技术干货、资讯、高薪职位等

 

扫码添加微信:haomei0452  领取千万学习优惠
前端疑难解答、学习咨询、视频资源、任何前端问题都会尽力帮你解决



访问 www.icketang.com 获取更多信息。