从网上看到的不到20行的js代码实现贪吃蛇代码可否仔细的解释一下?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>js贪吃蛇20行代码</title>
</head>
<body>
    <canvas id="can" width="400" height="400" style="background:black;"></canvas>
    <script>
    var sn=[42,41],dz=43,fx=1,n,ctx=document.getElementById("can").getContext("2d");
    function draw(t,c){
        ctx.fillStyle=c;
        ctx.fillRect(t%20*20,~~(t/20)*20,18,18);
    }
    document.onkeydown=function(e){fx=sn[1]-sn[0]==(n=[-1,-20,1,20][(e||event).keyCode-37]||fx)?fx:n};
    !function(){
        sn.unshift(n=sn[0]+fx);
        if(sn.indexOf(n,1)>0 || n<0||n>399||fx==1&&n%20==0||fx==-1&&n%20==19) return alert("GAME OVER");
        draw(n,"Lime");
        if(n==dz){
            while(sn.indexOf(dz=~~(Math.random()*400))>=0);
            draw(dz,"Yellow");
        }else
            draw(sn.pop(),"Black");
        setTimeout(arguments.callee,500);
    }();
    </script>
</body>
</html>

看了3个小时,好像看明白了一点,但是还是不是太清按上下键转换方向的那一块儿,还有

ctx.fillRect(t%20*20,~~(t/20)*20,18,18);
document.onkeydown=function(e){fx=sn[1]-sn[0]==(n=[-1,-20,1,20][(e||event).keyCode-37]||fx)?fx:n};

这两行代码怎么实现的?

阅读 4.5k
2 个回答

格式化一下代码就看懂了:

首先HTML部分:

<!-- 定义画布, 宽400, 高400, 背景#000 -->
<canvas id="can" width="400" height="400" style="background:black;"></canvas>

然后游戏部分:

var sn = [42, 41], // 存放贪食蛇的坐标, 第一个是头, 最后一个是尾, 初始的蛇长度是2, 坐标[2, 2], [1, 2]
  dz = 43, // 食物的坐标, 初始[3, 2]
  fx = 1, // 蛇前进的方向
  n, // 蛇头位置
  ctx = document.getElementById("can").getContext("2d"); // 画布
// 在画布上画背景或者贪食蛇方格
// 第一个参数是坐标信息, 第二个是颜色信息(用来区分背景和蛇)
function draw(t, c) {
  ctx.fillStyle = c;
  // fillReact的签名是: fillRect(x: number, y: number, w: number, h: number): void;
  // 所以画的方格的宽高都是18, 加上默认的lineWidth=1, 所以每个方格的长宽都是20
  // 参数t用一个标量标志二维坐标信息, t % 20即t/20的余数部分表示横坐标是第几个方格
  // <int>(t/20) 即t/20的商部分表示纵坐标是第几个方格
  // 这意味着游戏画布宽度固定为20, 坐标为[0, 19]
  // 需要注意的是纵坐标的方向向下
  // 所以初始的贪食蛇`[42, 41]`的头和尾的实际坐标分别是[2, 2], [1, 2], 也就是在画布的右上方
  // 所以最初的运动方向是向右
  // 所以前进方向`fx`的含义是: -1向左, -20右上, 1向右, 20向下
  ctx.fillRect(t % 20 * 20, ~~(t / 20) * 20, 18, 18);
}
// 监听用户按键, 按键时改变下一次paint时蛇的方向
document.onkeydown = function (e) {
  // 方向键 左, 上, 右, 下的keyCode分别为37, 38, 39, 40
  // 如果用户按下这几个键, 会改变蛇头下一次的位置到对应的方向上
  // 如果不是这几个键, 则不会变动
  n = [-1, -20, 1, 20][(e || event).keyCode - 37] || fx
  // 如果按键的方向和蛇当前前进的方向相反, 则不改动
  fx = sn[1] - sn[0] == n ? fx : n
};
// 根据蛇的位置及运动方向更新画布
!function update() {
  // 更新蛇头的位置
  n = sn[0] + fx;
  // 蛇每前进一步, 只需要把蛇尾放到蛇头处即可
  // 这里增加了蛇头
  sn.unshift(n);
  // 边界判断
  if (
    sn.indexOf(n, 1) > 0 // 蛇头碰到了自身
    || n < 0 // 或者纵坐标小于0
    || n > 399 // 或者纵坐标大于等于20
    || fx == 1 && n % 20 == 0 // 或者蛇向右运动并且蛇头横坐标是0(注意是更新过的坐标)
    || fx == -1 && n % 20 == 19 // 或者蛇头向左运动并且蛇头横坐标是19, 也就是在最右边
  ) {
    // 碰到边界时结束游戏, 边界是
    // x in [0, 20] & y in [0, 20]
    // 并且不可碰到蛇身
    return alert("GAME OVER");
  }
  // 更新蛇头
  draw(n, "Lime");
  if (n == dz) {
    // 如果蛇头位置和食物的位置相同, 则更新食物的位置
    // 新的食物的位置不能在蛇身上
    while (sn.indexOf(dz = ~~(Math.random() * 400)) >= 0);
    // 更新食物
    draw(dz, "Yellow");
  } else
  // 去掉蛇尾(画成背景)
    draw(sn.pop(), "Black");
  // 每隔500ms更新
  setTimeout(update, 500);
}();
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏