修改贪吃蛇中的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>

浙公网安备 33010602011771号