JavaScript+ Canvas开发趣味小游戏《贪吃蛇》

一、效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、《贪吃蛇》基本实现思路

在这里插入图片描述

  1. 蛇头部分+蛇身体部分:采用对象形式来存储坐标,并将每个坐标对象放到一个snake数组中,方便使用。设置每个方格宽度30px,高度30px画布高度600px,宽度600px
    a.新蛇头newHead等于旧蛇头oldHead的x坐标或者y坐标+1
    b.蛇的头部+身体部分,每次更新(动起来)的规律:每次将蛇的身体部分的最后1个方块(尾部)删除,然后在蛇的头部的前面增加1个方块:该新增的方块(newHead)的坐标与原来头部方块(oldHead)的坐标关系为 newHead等于旧蛇头oldHead的x坐标或者y坐标+1
    在这里插入图片描述

  2. 蛇吃食物
    a.当蛇头的坐标与食物的坐标重合的时候,表示食物被吃掉了。判断标准为:蛇头的x,y坐标都与食物的x,y坐标重合
    此时,要做两件事情:
    1)isEated 变为 true,此时就会重新生成一个食物坐标
    2)让蛇增加一节长度。但是,前面的1.b里提到,关于蛇动起来的规律中,每次更新画布都会把蛇的身体部分(尾巴)删掉1个方块,也就是当蛇没有吃到食物时,删除一节。所以,在这里蛇吃到食物时,只要不删除最后一节,也就相当于蛇吃到食物增加了一节了

  3. 绘制食物
    a.食物有默认位置
    b.食物没有被吃掉之前,不会动
    c.只有食物被吃掉后,才会随机再生成一个

  4. 绘制贪吃蛇
    在这里插入图片描述
    在这里插入图片描述
    a.贪吃蛇随着画布的 擦除->重绘的过程,会动起来(即改变贪吃蛇的坐标),这就是让蛇能够动起来的原理。这里需要配合定时器来实现动起来!
    b.贪吃蛇默认移动方向为:水平向右
    c.默认每次移动的距离为一个方格
    以下的x,y都是指蛇头的坐标只要蛇头的坐标发生改变,蛇的身体部分自然会跟着蛇头来运动
    水平往左 : x-1 , y不变
    垂直向下 : x不变 , y+1
    垂直向上 : x不变 , y-1

  5. 绘制网格:使用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

  6. 游戏结束的判断标准:
    贪吃蛇超出上、下、左、右边界时,游戏结束
    即newHead.y < 0 || newHead.x < 0 || newHead.x * 30 >= 600 || newHead.y * 30 >= 600时,游戏结束。

三、技术要点

  1. 使用Canvas****绘制线条 tools.moveTo(0, 30 * i + 0.5) 、 tools.lineTo(600, 30 * i + 0.5)实心矩形tools.fillRect(x,y,width,height),并设置它们的颜色
  2. setInterval定时器的使用以及清除。var StopTime = setInterval(timer, 100)clearInterval(StopTime)
  3. 数组元素的尾部删除pop()和头部新增unshift()
  4. **Math.random()**随机数的使用,该方法生成[0,1)的随机数,包含0不包含1
  5. 监听键盘按下的行为,
document.**addEventListener**('keydown', function(event) {
if (event.keyCode === 38) {
						//如果键值为38,就说明按下了 上键
						console.log("上")
						directionX = 0
						directionY = -1
					}}
  1. JavaScript操作Dom对象,获取相应的Dom元素
  2. 自定义计时函数
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
  2. 当蛇头触碰到游戏边界时,游戏结束
  3. 分数记录,每吃到一个一个食物,分数加1
  4. 游戏总时长记录

五、代码如下:

<!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>

猜你喜欢

转载自blog.csdn.net/weixin_44827418/article/details/106303060