canvas网页版贪吃蛇(完整教程)

版权声明:本文为博主原创文章,允许转载,但转载必须注明出处并附带首发链接 https://blog.csdn.net/Nana_9457/article/details/86570234

源码见github: https://github.com/NanaYang007/Canvas__DoodleSnake
喜欢请给个star,谢谢~

一起来实现一个贪吃蛇吧~

0. 创建canvas获取绘制环境

首先在网页中引入canvas标签,注意有些浏览器不支持canvas,可以给出提示。

<html>
  <meta charset="utf8">
  <style>
	  body {
		  background-color: black;
	  }
	  canvas {
		  background-color: white;
	  }
  </style>
<body>
	<canvas id="mySnake" width="400" height="300">
		<span>该浏览器不支持canvas</span>
	</canvas>
	<script>
		var cv = document.getElementById("mysnake");
		var ctx = cv.getContext("2d");
	</script>
</body>
</html>
1. 绘制方块
// 小方块的大小设置为常量,便于修改
const STEP = 10;
// 四个参数分别为(距canvas左边缘距离、距上边缘距离、矩形宽、矩形高)
ctx.fillRect(10, 10, STEP, STEP);
2. 计时器

贪吃蛇作为一个动态游戏,需要一个定时器
⚠️设置高度时不能使用var top = 0,top是window上的一个对象,会出现canvas不显示动态方块的bug(控制台无报错)

var blockLeft = STEP;
// 变量名不能设置为“top”
var blockTop = STEP;
var stepLength = STEP;
setInterval(function(){
	// 每次重画前需要清画布
	ctx.clearRect(cv.offsetLeft, cv.offsetTop, cv.offsetWidth, cv.offsetHeight);
	blockLeft += stepLength;
	blockTop += stepLength;
	console.log(top);
	ctx.fillRect( blockLeft, blockTop, STEP, STEP);
}, 300);

现在我们就得到一个从左上角不断向右下方跳的小黑方块了~

3. 获取鼠标事件

蛇的运动方向是用户通过操作键盘的“上、下、左、右”按键实现的,所以需要获取到键盘点击事件,
另外,每一步向左(右)、向上(下)运动的步距也是点击一次就改变并维持到下一次点击键盘的。
所以首先需要将stepLength变成有两个方向(包含正负属性)的变量

// var stepLength = STEP;
var stepLeft = STEP;
var stepTop = STEP;

然后获取键盘点击事件:

blockLeft += stepLeft;
blockTop += stepTop;
document.onkeyup = function(e){
	var e = e || widnow.event;
	// 左 37  上 38  右 39  下 40(自行补齐)
	if( e.keyCode == 37){
		stepLeft = - STEP;
		stepTop = STEP;
	}
}
4. 设置撞墙情况

canvas除了可以绘制图形,还可以绘制文字

ctx.font = 'bold 25px Arial';
ctx.fillText("Game Over", 100, 100, 300);

定时器在撞墙后需要关闭,所以需要定义一下定时器名字

var timer = setInterval(function(){...}, 300);

为了让撞墙的小方块停在那里,需要将清除画布的逻辑放到判断撞墙这里

// 撞墙
if( blockLeft <= cv.offsetLeft || blockLeft >= cv.offsetLeft + cv.offsetWidth - STEP ||
	blockTop <= cv.offsetTop || blockTop >= cv.offsetTop + cv.offsetHeight - STEP){
	clearInterval(timer);
	ctx.font = 'bold 25px Arial';
	ctx.fillText("Game Over", 100, 100, 300);
} else {
	// 每次重画前需要清画布
	ctx.clearRect(cv.offsetLeft, cv.offsetTop, cv.offsetWidth, cv.offsetHeight);
}
5. 单个方块变成list

首先,需要建立一个list,并初始化它的位置

const SNAKE_LENGTH = 4; // 蛇的长度(方块数量)
var snake = [];
// 初始化list
for(var i = SNAKE_LENGTH - 1; i >= 0; i--){
	snake.push({
		left: STEP,
		top: i * STEP
	});
}

每次绘制方块变成绘制list

// ctx.fillRect( blockLeft, blockTop, STEP, STEP);
snake.forEach(function(item){
	ctx.fillRect(item.left, item.top, STEP, STEP);
})

初始化位移方向

snake.forEach(function(item){
	item.left += stepLeft;
	item.top += stepTop;
})

判断撞墙情况需要通过snake的头部节点的left/top判断

// 撞墙
if( snake[0].left <= cv.offsetLeft || snake[0].left >= cv.offsetLeft + cv.offsetWidth - STEP ||
snake[0].top <= cv.offsetTop || snake[0].top >= cv.offsetTop + cv.offsetHeight - STEP){}
6. 处理蛇的多次转弯

贪吃蛇的行为是:键盘点击后,蛇头的运动方向改变了,但是蛇身的运动方向还是原来的,直到蛇身的某一点到达鼠标点击时蛇头的位置(设为转折点),运动方向才切换为蛇头的运动方向。
用户多次点击键盘蛇身随时间推移会表现出多次转弯效果。

算法有多种实现,如考虑到转折点、将方向存成列表计算区间等,可以设计出不同时空复杂度的方法,此处不考虑空间复杂度,实现时间复杂度为o(1)的方案:蛇头判断是否改变方向,蛇尾擦除一个方块。

var snakeHead = {
	left: snake[0].left,
	top: snake[0].top
};
snake.unshift({
	left: snakeHead.left + stepLeft,
	top: snakeHead.top + stepTop
});
snake.pop();
7. 加入食物?

首先障碍物的位置随机,需要js的Math.random(),生成0.0~1.0之间的随机数。

// timer外部:
// 添加食物
var foodX = Math.ceil(Math.random() * (cv.offsetWidth / 10)) * 10;
var foodY = Math.ceil(Math.random() * (cv.offsetHeight / 10)) * 10;
var foodColor = "#" + Math.ceil(Math.random() * 255).toString(16) + Math.ceil(Math.random() * 255).toString(16) + Math.ceil(Math.random() * 255).toString(16);  
var snakeColor = "black";

// timer内部:
//吃到食物
if(snake[0].left == foodX && snake[0].top == foodY){
	snakeColor = foodColor;
	snake.push({
		left: snake[snake.length - 1].left,
		top: snake[snake.length - 1].top
	});
	foodX = Math.ceil(Math.random() * ( (cv.offsetWidth - 100) / 10)) * 10 + 50;
	foodY = Math.ceil(Math.random() * ( (cv.offsetHeight - 100 ) / 10)) * 10 + 50;
	foodColor = "#" + Math.ceil(Math.random() * 255).toString(16) + Math.ceil(Math.random() * 255).toString(16) + Math.ceil(Math.random() * 255).toString(16); 
}
snake.forEach(function(item, index){
	ctx.save();
	ctx.fillStyle = snakeColor;
	ctx.fillRect(item.left, item.top, STEP, STEP);
	ctx.restore();
});
// 添加食物
ctx.save();
ctx.fillStyle = foodColor;
ctx.fillRect(foodX, foodY, STEP, STEP);
ctx.restore();
		

实现效果如下:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值