修改贪吃蛇中的Bug

修复 BUG 阶段

 

bug 1

我们贪吃蛇移动的时候

我们如果使用了 W A S D 键会造成这个 bug

如图所示

 

贪吃蛇消失了一部分

JS 代码中我们是这样写的

 document.addEventListener('keydown', (event) => {
     dirc = [-1, -20, 1, 20][event.keyCode - 37]
     console.log(dirc)
 })

这里我们只做了判断 键作为判断角度

而其他的键盘按下, snake 角度是 undefined

所以我们要做出改变

首先我们要让我们按下的键值 只包含在 键里面

才执行这个函数

    dirc = [-1, -20, 1, 20][event.keyCode - 37]

我们声明一个变量名为 kcode 用来定义我们每次按下键盘所触发的 keyCode

代码如下

  let kcode = event.keyCode  //声明一个变量是我们按下的keyCode

要让他包含的话

我们在数组中我们可以想到用 includes方法

然后我们再做 if 判断

完整修改代码如下

 document.addEventListener('keydown', (event) => {
     let kcode = event.keyCode  //声明一个变量是我们按下的keyCode
     if([37,38,39,40].includes(kcode)){
         dirc = [-1, -20, 1, 20][event.keyCode - 37]
         console.log(dirc)
    }
 
 })

 

bug 2 碰壁死亡检测

首先我们

 

垂直移动的碰撞检测

很简单,只需要我们的蛇头部分

在垂直向上的时候 next<0 就触发死亡

在垂直向下的时候 next>600 就触发死亡

 

水平移动的碰撞检测

因为我们 next 是下图

 let next = snake[0] + dirc

我们蛇头部分 是 snake[0] 加上 一个 dirc

我们知道当

dirc=1的时候我们是正向移动

dirc=-1的时候我们是负向移动

next里面是 + 了 dirc

所以

贪吃蛇实际移动的原理是什么

在上面我们有这一张图

 

就像一个个排列好的灯管序列

从第一行开始亮

一直移动在接下来的每一行

这样根据图,就很清晰了

 

其实我们在写完代码的时候我们就可以明白这个是什么意思了

 

它的头部撞到右面墙死亡的时候其实头部到了下一行的第一个

 

同理在撞到左面墙的时候也是,头部到了上一行的最后一个

 

dirc=1 时 我们需要满足 (dirc == 1 && next % g_col == 0)

 

dirc=-1 时 我们需要满足 (dirc == -1 && next % g_col == (g_col - 1))

 

这样我们需要满足四个条件之一就可以实现碰壁死亡了

 

实现死亡,我们让蛇停止运行

也就是

我们让定时器失效 也就是 clearInterval

 

这样的话,我们将定时器封装成一个函数 Move,以便我们反复调用

代码如下

 var timer;
 timer = setInterval(Move, 500)
 
 function Move() {
 
         //贪吃蛇的增添
         let next = snake[0] + dirc
         console.log(next)
 }

 

上面的四个条件判断我们都进行声明

分别为 dir1 dir2 dir3 dir4

这样我们需要满足四个条件之一就可以实现碰壁死亡了

if 条件成立,我们就清除定时器,并且 alert 弹窗 游戏结束

 

完整代码如下

 

 var timer;
 timer = setInterval(Move, 500)
 
 function Move() {
 
     //贪吃蛇的增添
     let next = snake[0] + dirc
     console.log(next)
     //边界问题:四个方向
     //垂直移动
     let dir1 = (next < 0)
     let dir2 = (next > g_col * g_row)
     //水平移动
     let dir3 = (dirc == 1 && next % g_col == 0)
     let dir4 = (dirc == -1 && next % g_col == (g_col - 1))
     if (dir1 || dir2 || dir3 || dir4) {
         alert("游戏结束")
         clearInterval(timer)
    }

 

bug 3 食物的随机生成

我们上面提到食物的生成是随机的,但是这个随机性

可能会出现这样的一个结果

就是食物可能会随机生成在蛇的身体上,虽然几率很小,我们也要避免这个bug

思路

我们在食物生成的时候我们进行循环检测

这里我们用 do while 循环和 includes 方法 来判断随机生成的食物是否在蛇这个数组中

修改代码如下

     do {
         food = ~~(Math.random() * g_col * g_row)
         //循环体
        }
         //在其他地方生成新的食物,并绘制游戏块:食物
         while (snake.includes(food))
 

 

bug 4 游戏结束后,点击确定没有重新开始游戏

我们只要在alert弹窗出来之后,让我们的页面进行刷新就可以了

代码如下

 window.location.reload();




完整代码展示

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>复刻经典</title>
    <style>
        #myCanvas {
            background: #89cff0;
            box-shadow:0 1px 10px #3e3e3e ;
        }
    </style>
</head>
<body>
<canvas id="myCanvas" width="600" height="600"></canvas>
</body>
</html>
<script>    var canv = document.getElementById('myCanvas')    //获取前端的Dom
var ctx = canv.getContext('2d')    //在2d平面上进行绘画
//代码思路
//贪吃蛇:蛇是一个数组[id]  食物:id   移动方向:上下左右
//游戏块布局:按行列布局 20*20 = 400 id:0~399
//移动: 左 -1,上, -20 , 右 +1 , 下 +20
//这样我们用键盘事件keyCode来实现他们
//左键:keyCode:37   上键:keyCode:38    右键:keyCode:39    下键:keyCode:40

//如何解析id  从而确定每个方块的坐标呢

// 我们思考  第一行    都是<20的数  坐标 应该是(x,0)

//而 0 我们应该是根据他这一行的数对20取整  得到的整数部分就是他的纵坐标 0
//同理第二行 对20取整,得到的整数部分是 1 ,2行,3行也是如此、

//而这个每个方块的横坐标呢

//那就是取余  就解决了

//综上所述

//解析id获取坐标         行号row:~~(id/20)  列号col:id%20

//坐标x,y   x=col*块宽    y=row*块高


//实现移动
//在不吃到食物实现贪吃蛇的移动

//根据移动方向,生成新的头部块next=snake[0]+方向
//将生成的next添加到原来的snake数组的头部   我们用到unshift() 方法
//并且我们要删除尾部元素   以实现在不吃到食物的状态下,体长不变化     我们这用到pop()方法

//如果吃到食物的话     我们不删除尾部元素    并且在其他的地方生成新的食物

// ### 键盘事件
//
// 之前我们说到  `KeyCode` :左:37   上:38   右:39    下:40
//
// 对应到贪吃蛇的                             左:-1    上:-20   右:+1   下:+20
//
// 贪吃蛇的方向数组为[-1 , -20, 1 , 20] 每个数组的`index` 为 0 , 1 , 2 , 3
//
// 那么这个  `index`  怎么和这个`KeyCode`取得联系呢
//
// 我们可以想到   `index`  为`[KeyCode-37]`
var g_block = 30
var g_row = 600 / g_block
var g_col = 600 / g_block
var g_margin = 1

var snake = [45, 44, 43]  //蛇是由53,54,55游戏块组成的
var food = 55   //自定义初始食物为58号游戏块
var dirc = 1   //定义方向是向右移动1个单位
var colors = ['#f5b6b3', '#ffebb5', '#89cff0']  //声明蛇的颜色 食物的颜色 和 背景颜色
function setBlock(id, color) {
    //我们声明setBlock函数
    let [row, col] = [~~(id / g_col), id % g_col]//为他的row和col赋值
    //~~取整的意思
    let [x, y] = [col * g_block, row * g_block]//对他的坐标赋值
    ctx.save()
    ctx.fillStyle = colors[color]
    ctx.fillRect(x, y, g_block - g_margin, g_block - g_margin)
    ctx.restore()
}

snake.forEach(n => setBlock(n, 0))
// 将蛇包含的三个方块都进行染色
setBlock(food, 1)
//id为 food  color:第一个元素
//将food绘画出来
var timer;
timer = setInterval(Move, 500)

function Move() {
    //贪吃蛇的增添
    let next = snake[0] + dirc
    console.log(next)
    //边界问题:四个方向
    //垂直移动
    let dir1 = (next < 0)
    let dir2 = (next > g_col * g_row)
    //水平移动
    let dir3 = (dirc == 1 && next % g_col == 0)
    let dir4 = (dirc == -1 && next % g_col == (g_col - 1))
    if (dir1 || dir2 || dir3 || dir4) {
        alert("游戏结束")
        clearInterval(timer)

        window.location.reload();
    }
    snake.unshift(next)
    setBlock(next, 0)
    //id 为next  color: 为第零个元素
    if (next == food) {
        do {
            food = ~~(Math.random() * g_col * g_row)
        }
            //在其他地方生成新的食物,并绘制游戏块:食物
        while (snake.includes(food))
        setBlock(food, 1)
    } else {
        let tail = snake.pop()
        setBlock(tail, 2)
        //id  为tail color 为第二个元素
    }
    // 但是我们该怎么让他动起来呢
    //我们想到用定时器
}

//键盘事件实现移动
document.addEventListener('keydown', (event) => {
    let kcode = event.keyCode  //声明一个变量是我们按下的keyCode
    if ([37, 38, 39, 40].includes(kcode)) {
        dirc = [-1, -20, 1, 20][event.keyCode - 37]
        console.log(dirc)
    }
})
</script>

  

 
 
 
posted @ 2022-05-06 14:49  doudou帅  阅读(266)  评论(0)    收藏  举报