一、效果展示
二、《贪吃蛇》基本实现思路
-
蛇头部分+蛇身体部分:采用对象形式来存储坐标,并将每个坐标对象放到一个snake数组中,方便使用。设置每个方格宽度30px,高度30px,画布高度600px,宽度600px。
a.新蛇头newHead等于旧蛇头oldHead的x坐标或者y坐标+1
b.蛇的头部+身体部分,每次更新(动起来)的规律:每次将蛇的身体部分的最后1个方块(尾部)删除,然后在蛇的头部的前面增加1个方块:该新增的方块(newHead)的坐标与原来头部方块(oldHead)的坐标关系为 newHead等于旧蛇头oldHead的x坐标或者y坐标+1
-
蛇吃食物
a.当蛇头的坐标与食物的坐标重合的时候,表示食物被吃掉了。判断标准为:蛇头的x,y坐标都与食物的x,y坐标重合
此时,要做两件事情:
1)isEated 变为 true,此时就会重新生成一个食物坐标
2)让蛇增加一节长度。但是,前面的1.b里提到,关于蛇动起来的规律中,每次更新画布都会把蛇的身体部分(尾巴)删掉1个方块,也就是当蛇没有吃到食物时,删除一节。所以,在这里蛇吃到食物时,只要不删除最后一节,也就相当于蛇吃到食物增加了一节了。 -
绘制食物
a.食物有默认位置
b.食物没有被吃掉之前,不会动
c.只有食物被吃掉后,才会随机再生成一个 -
绘制贪吃蛇
a.贪吃蛇随着画布的 擦除->重绘的过程,会动起来(即改变贪吃蛇的坐标),这就是让蛇能够动起来的原理。这里需要配合定时器来实现动起来!
b.贪吃蛇默认移动方向为:水平向右
c.默认每次移动的距离为一个方格
以下的x,y都是指蛇头的坐标,只要蛇头的坐标发生改变,蛇的身体部分自然会跟着蛇头来运动
水平往左 : x-1 , y不变
垂直向下 : x不变 , y+1
垂直向上 : x不变 , y-1 -
绘制网格:使用Canvas来实现
注意Canvas使用方法:
1)获取画布对象 var huabu = document.getElementById(‘huabu’)
2)获取画布对象中的工具箱 var tools = huabu.getContext(‘2d’)
3)从画图工具箱中取出要使用的工具 tools.moveTo(0, 30 * i + 0.5) 、 tools.lineTo(600, 30 * i + 0.5)
4)找坐标
5)绘制
注意:在使用Canvas绘制线条时坐标的设置,如果要设置的线条粗细为1px,则需要把tools.moveTo()、 tools.lineTo()里面的起点终点坐标设置成.5小数形式,否则画出来的线条粗细为2px -
游戏结束的判断标准:
贪吃蛇超出上、下、左、右边界时,游戏结束
即newHead.y < 0 || newHead.x < 0 || newHead.x * 30 >= 600 || newHead.y * 30 >= 600时,游戏结束。
三、技术要点
- 使用Canvas****绘制线条 tools.moveTo(0, 30 * i + 0.5) 、 tools.lineTo(600, 30 * i + 0.5)、实心矩形tools.fillRect(x,y,width,height),并设置它们的颜色
- setInterval定时器的使用以及清除。var StopTime = setInterval(timer, 100)、clearInterval(StopTime)
- 数组元素的尾部删除pop()和头部新增unshift()
- **Math.random()**随机数的使用,该方法生成[0,1)的随机数,包含0不包含1
- 监听键盘按下的行为,
document.**addEventListener**('keydown', function(event) {
if (event.keyCode === 38) {
//如果键值为38,就说明按下了 上键
console.log("上")
directionX = 0
directionY = -1
}}
- JavaScript操作Dom对象,获取相应的Dom元素
- 自定义计时函数
var h = m = s = ms = 0; //定义时,分,秒,毫秒并初始化为0;
function toDub(n) { //补0操作
if (n < 10) {
return "0" + n;
} else {
return "" + n;
}
}
function toDubms(n) { //给毫秒补0操作
if (n < 10) {
return "00" + n;
} else {
return "0" + n;
}
}
function timer() { //定义计时函数
ms = ms + 50; //毫秒
if (ms >= 1000) {
ms = 0;
s = s + 1; //秒
}
if (s >= 60) {
s = 0;
m = m + 1; //分钟
}
if (m >= 60) {
m = 0;
h = h + 1; //小时
}
str = toDub(h) + "时" + toDub(m) + "分" + toDub(s) + "秒" + toDubms(ms) + "毫秒";
timeDom.innerHTML = str;
// document.getElementById('mytime').innerHTML=h+"时"+m+"分"+s+"秒"+ms+"毫秒";
}
四、游戏功能:
- 每次吃到食物时蛇的长度加1
- 当蛇头触碰到游戏边界时,游戏结束
- 分数记录,每吃到一个一个食物,分数加1
- 游戏总时长记录
五、代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>贪吃蛇游戏</title>
</head>
<style type="text/css"> * { margin: 0; padding: 0; } a { text-decoration: none; } canvas { position: relative; display: block; margin: 0 auto; background-color: #33cc99; } .images { background: url(img/timg.jpg) no-repeat; width: 800px; height: 600px; position: absolute; top: 0; left: 50%; margin-left: -400px; } .start { width: 160px; height: 40px; border: 1px solid #33CC99; border-radius: 10px; color: #fff; background-color: orangered; font-size: 25px; text-align: center; line-height: 40px; position: absolute; bottom: 200px; left: 50%; margin-left: -80px; } .author { width: 160px; position: absolute; bottom: 150px; left: 50%; margin-left: -80px; text-align: center; font-weight: 600; font-size: 18px; } .over { background: url(img/over.jpg) no-repeat; width: 800px; height: 600px; position: absolute; top: 0; left: 50%; margin-left: -400px; } .black_overlay { display: none; position: absolute; top: 0%; left: 0%; width: 100%; height: 100%; background-color: black; z-index: 1001; -moz-opacity: 1; opacity: 1; filter: alpha(opacity=1); } .black_overlay>img { display: none; margin: 0 auto; margin-top: 190px; } .black_overlay>a { width: 160px; height: 40px; border: 1px solid #33CC99; border-radius: 10px; color: #fff; background-color: orangered; font-size: 25px; text-align: center; line-height: 40px; position: absolute; bottom: 230px; left: 50%; margin-left: -80px; } #score { margin-left: 500px; } #scores { margin-right: 200px; } </style>
<body>
<canvas id="huabu" width="600" height="600"></canvas>
<div class="images" id="images">
<div class="start" id="start">开始游戏</div>
<div class="author">制作人:Zep</div>
</div>
<div id="fade" class="black_overlay">
<img src="./img/over.jpg" id="over">
<a href="index.html">重新开始</a>
</div>
<div id="score">
分数:<span id="scores">0</span>
游戏时长:<span id="time">00时00分00秒0000毫秒</span>
</div>
</body>
<script type="text/javascript"> // 是否开始游戏 var isStart = false // 点击start按钮,隐藏首页图片 var startbtn = document.getElementById('start') var imagesDom = document.getElementById('images') var fadeDom = document.getElementById('fade') var overDom = document.getElementById('over') var score = 0.0 var speed = 3 var scoresDom = document.getElementById('scores') var timeDom = document.getElementById('time') var h = m = s = ms = 0; //定义时,分,秒,毫秒并初始化为0; function toDub(n) { //补0操作 if (n < 10) { return "0" + n; } else { return "" + n; } } function toDubms(n) { //给毫秒补0操作 if (n < 10) { return "00" + n; } else { return "0" + n; } } function timer() { //定义计时函数 ms = ms + 50; //毫秒 if (ms >= 1000) { ms = 0; s = s + 1; //秒 } if (s >= 60) { s = 0; m = m + 1; //分钟 } if (m >= 60) { m = 0; h = h + 1; //小时 } str = toDub(h) + "时" + toDub(m) + "分" + toDub(s) + "秒" + toDubms(ms) + "毫秒"; timeDom.innerHTML = str; // document.getElementById('mytime').innerHTML=h+"时"+m+"分"+s+"秒"+ms+"毫秒"; } startbtn.onclick = function() { imagesDom.style.display = 'none' isStart = true } var Game = setInterval(gameStart, 500) function gameStart() { if (isStart) { clearInterval(Game) var StopTime = setInterval(timer, 100) // console.log('111') // 1.获取画布对象 var huabu = document.getElementById('huabu') // 2.获取画布对象中的工具箱 var tools = huabu.getContext('2d') //注意:1.食物有默认位置 // 2.食物没有被吃掉之前,不会动 // 3.只有食物被吃掉后,才会随机再生成一个 // 随机生产x,y坐标 // 食物的默认位置 var x = Math.floor(Math.random() * 20) * 30 var y = Math.floor(Math.random() * 20) * 30 // 添加一个标记,记录食物是否被吃掉了 var isEated = false // 默认食物没有被吃掉 // 蛇的默认位置 var snake = [{ x: 3, y: 0 }, { x: 2, y: 0 }, { x: 1, y: 0 }] // 蛇的默认方向:水平向右 var directionX = 1 var directionY = 0 // 判断游戏是否结束 var isGameOver = false // 监听键盘按下的行为 document.addEventListener('keydown', function(event) { console.log('按键的键值', event.keyCode) // 上:38,下:40,左:37,右:39 if (event.keyCode === 38) { //如果键值为38,就说明按下了 上键 console.log("上") directionX = 0 directionY = -1 } else if (event.keyCode === 40) { //如果键值为40,就说明按下了 下键 console.log("下") directionX = 0 directionY = 1 } else if (event.keyCode === 37) { //如果键值为37,就说明按下了 左键 console.log("左") directionX = -1 directionY = 0 } else if (event.keyCode === 39) { //如果键值为39,就说明按下了 右键 console.log("右") directionX = 1 directionY = 0 } }) // 使用定时器,让贪吃蛇动起来 // 定时器每隔一段时间,就执行一遍“擦除画布->重绘画布”的过程 // 以下代码表示:1s执行三次 “擦除画布->重绘画布”的过程 setInterval(function() { // 如果isGameOver = false 说明游戏结束了,执行return,就不执行下面的代码了 if (isGameOver) { fadeDom.style.display = 'block' overDom.style.display = 'block' clearInterval(StopTime) return } // 一、擦除画布 tools.clearRect(0, 0, 600, 600) // 二、以下代码就是重绘画布的代码 // -----------------------A.蛇头部分开始--------------------------- // A.蛇头部分 var oldHead = snake[0] var newHead = { x: oldHead.x + directionX, y: oldHead.y + directionY } //游戏结束的判定: // 1.蛇头的y<0,贪吃蛇超出上边界,游戏结束 if (newHead.y < 0 || newHead.x < 0 || newHead.x * 30 >= 600 || newHead.y * 30 >= 600) { // 游戏结束 isGameOver = true } else { // 在数组首部添加一节,结果为:(4,0)(3,0) (2,0) snake.unshift(newHead) //在数组第一个元素前面添加一个元素 // -----------------------B.蛇吃食物开始--------------------------- // 蛇吃食物的分析: // 当蛇头的坐标与食物的坐标重合的时候,表示食物被吃掉了 // 此时,要做两件事情: // 1.isEated 变为 true,此时就会重新生成一个食物坐标 if (snake[0].x * 30 === x && snake[0].y * 30 === y) { // 蛇头的x,y坐标都与食物的x,y坐标重合 isEated = true } else { // 2.让蛇增加一节长度,也就是当蛇没有吃到食物时,删除一节,吃到食物时,就不删除一节,也就相当于蛇吃到食物增加一节 // 删除最后一节,结果为:(3,0) (2,0) snake.pop() //删除数组最后一个元素 // 重新设置食物没有被吃掉的标志 isEated = false } // -----------------------蛇吃食物结束--------------------------- } // -----------------------蛇头部分结束--------------------------- // -----------------------C.绘制食物开始--------------------------- // 绘制矩形 tools.fillRect(x,y,width,height) //注意:1.食物有默认位置 // 2.食物没有被吃掉之前,不会动 // 3.只有食物被吃掉后,才会随机再生成一个 if (isEated) { x = Math.floor(Math.random() * 20) * 30 y = Math.floor(Math.random() * 20) * 30 score += 1 speed += 0.1 } scoresDom.innerHTML = score // 设置填充颜色 tools.fillStyle = '#cccc00' tools.fillRect(x, y, 30, 30) // -----------------------绘制食物结束--------------------------- // -----------------------D.绘制贪吃蛇开始--------------------------- // 说明: // 1.贪吃蛇随着 擦除->重绘的过程,会动起来(即改变贪吃蛇的坐标) // 2.贪吃蛇默认移动方向为:水平向右 // 3.默认每次移动的距离为一个方格 // 问题:水平往左 : x-1 , y不变 // 垂直向下 : x不变 , y+1 // 垂直向上 : x不变 , y-1 // // 删除最后一节,结果为:(3,0) (2,0) // snake.pop() //删除数组最后一个元素 // 绘制蛇头部,蛇的每一节都是一个矩形 // // 设置蛇头的颜色 // tools.fillStyle = '#ff0033' // tools.fillRect(snake[0].x * 30,snake[0].y * 30,30,30) // // 绘制蛇的身体部分 // tools.fillStyle = '#333399' // tools.fillRect(snake[1].x * 30,snake[1].y * 30,30,30) // tools.fillRect(snake[2].x * 30,snake[2].y * 30,30,30) for (var i = 0; i < snake.length; i++) { if (i === 0) { tools.fillStyle = '#ff0033' } else { tools.fillStyle = '#333399' } tools.fillRect(snake[i].x * 30, snake[i].y * 30, 30, 30) } // -----------------------绘制贪吃蛇结束--------------------------- // -----------------------E.绘制网格开始--------------------------- // 3.从画图工具箱中取出要使用的工具 // 4.找坐标 // 绘制水平线 for (var i = 1; i < 20; i++) { tools.moveTo(0, 30 * i + 0.5) tools.lineTo(600, 30 * i + 0.5) } // 绘制垂直线 for (var i = 1; i < 20; i++) { tools.moveTo(30 * i + 0.5, 0) tools.lineTo(30 * i + 0.5, 600) } // 设置绘制直线的颜色 tools.strokeStyle = '#fff' // 5.绘制 tools.stroke() // -----------------------绘制网格结束--------------------------- }, 1000 / speed) } } </script>
</html>