前言
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心~
又很多朋友最近说想要入门 Canvas
,问我能不能出一篇讲解 Canvas
的文章,但是我感觉直接讲会比较无聊,还不如通过一些案例来让大家去手敲呢
所以我准备使用 Canvas
实现一个贪吃蛇的小游戏,希望大家能喜欢~
效果 & 实现步骤
效果是下面这样
大概的实现步骤是这样的:
第一步:先把蛇画出来 第二步:让蛇能动起来 第三步:在图中随机找地方随机投放食物 第四步:控制蛇去吃这些食物 第五步:检测蛇撞到边缘
先把蛇画出来
实际上,绘制蛇的过程非常简单。蛇主要由蛇头和蛇身构成,这两部分都可以通过 正方形
格子来呈现。蛇头就是单独的一个方格,而蛇身则可以由多个方格组成
要绘制这些方格,我们可以使用 ctx.fillRect
方法。在这个过程中,我们用变量 head
来表示蛇头,而用 数组body
来存储蛇身的各个方格
让蛇能动起来
蛇的运动可以分为两种情形:
第一种,当游戏开始时,蛇会自动向右方移动; 第二种,玩家可以通过键盘的方向键来操控蛇,使其朝不同的方向行进。不论哪一种情况,蛇每秒钟都会前进一个方格的距离。
要让蛇动起来其实很容易理解,我就拿蛇向右移动这个情况来说明一下吧:
1、蛇头首先向右移动一个方格的距离,而蛇身的其余部分保持不动。 2、在蛇身的最前端添加一个新的方格。 3、移除蛇身尾部的方格。 4、通过使用定时器,实现蛇看起来像是在不断向右移动的效果。
实现效果如下:
在图中随机找地方随机投放食物
在画布上随机生成食物,即随机绘制一个方格,但需注意以下事项:
1、确保所选坐标位于画布的有效范围内 2、食物的位置不能与蛇身或蛇头重叠(否则蛇可能会被“砸晕”,哈哈)
效果如下,随机食物画出来了:
控制蛇去吃这些食物
蛇吃食物的原理很容易理解,就是当蛇头的坐标与食物的坐标重合时,就算蛇吃到食物了。这里有两点需要注意:
1、一旦蛇吃到食物,它的身体就要增长一个方格的长度。 2、蛇吃到食物之后,之前食物的位置就需要重新随机生成。
检测蛇撞到边缘
众所周知,蛇头碰到边界,或者碰到蛇身,都会终止游戏
自此,贪吃蛇🐍小游戏完成喽:
完整代码
draw()
function draw() {
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
// 定义一个全局的是否吃到食物的一个变量
let isEatFood = false
// 小方格的构造函数
function Rect(x, y, width, height, color) {
this.x = x
this.y = y
this.width = width
this.height = height
this.color = color
}
Rect.prototype.draw = function () {
ctx.beginPath()
ctx.fillStyle = this.color
ctx.fillRect(this.x, this.y, this.width, this.height)
ctx.strokeRect(this.x, this.y, this.width, this.height)
}
// 蛇的构造函数
function Snake(length = 0) {
this.length = length
// 蛇头
this.head = new Rect(canvas.width / 2, canvas.height / 2, 40, 40, 'red')
// 蛇身
this.body = []
let x = this.head.x - 40
let y = this.head.y
for (let i = 0; i < this.length; i++) {
const rect = new Rect(x, y, 40, 40, 'yellow')
this.body.push(rect)
x -= 40
}
}
Snake.prototype.drawSnake = function () {
// 如果碰到了
if (isHit(this)) {
// 清除定时器
clearInterval(timer)
const con = confirm(`总共吃了${this.body.length - this.length}个食物,重新开始吗`)
// 是否重开
if (con) {
draw()
}
return
}
// 绘制蛇头
this.head.draw()
// 绘制蛇身
for (let i = 0; i < this.body.length; i++) {
this.body[i].draw()
}
}
Snake.prototype.moveSnake = function () {
// 将蛇头上一次状态,拼到蛇身首部
const rect = new Rect(this.head.x, this.head.y, this.head.width, this.head.height, 'yellow')
this.body.unshift(rect)
// 判断蛇头是否与食物重叠,重叠就是吃到了,没重叠就是没吃到
isEatFood = food && this.head.x === food.x && this.head.y === food.y
// 咱们上面在蛇身首部插入方格
if (!isEatFood) {
// 没吃到就要去尾,相当于整条蛇没变长
this.body.pop()
} else {
// 吃到了就不去尾,相当于整条蛇延长一个方格
// 并且吃到了,就要重新生成一个随机食物
food = randomFood(this)
food.draw()
isEatFood = false
}
// 根据方向,控制蛇头的坐标
switch (this.direction) {
case 0:
this.head.x -= this.head.width
break
case 1:
this.head.y -= this.head.height
break
case 2:
this.head.x += this.head.width
break
case 3:
this.head.y += this.head.height
break
}
}
document.onkeydown = function (e) {
// 键盘事件
e = e || window.event
// 左37 上38 右39 下40
switch (e.keyCode) {
case 37:
console.log(37)
// 三元表达式,防止右移动时按左,下面同理(贪吃蛇可不能直接掉头)
snake.direction = snake.direction === 2 ? 2 : 0
snake.moveSnake()
break
case 38:
console.log(38)
snake.direction = snake.direction === 3 ? 3 : 1
break
case 39:
console.log(39)
snake.direction = snake.direction === 0 ? 0 : 2
break
case 40:
console.log(40)
snake.direction = snake.direction === 1 ? 1 : 3
break
}
}
function randomFood(snake) {
let isInSnake = true
let rect
while (isInSnake) {
const x = Math.round(Math.random() * (canvas.width - 40) / 40) * 40
const y = Math.round(Math.random() * (canvas.height - 40) / 40) * 40
console.log(x, y)
// 保证是40的倍数啊
rect = new Rect(x, y, 40, 40, 'blue')
// 判断食物是否与蛇头蛇身重叠
if ((snake.head.x === x && snake.head.y === y) || snake.body.find(item => item.x === x && item.y === y)) {
isInSnake = true
continue
} else {
isInSnake = false
}
}
return rect
}
function isHit(snake) {
const head = snake.head
// 是否碰到左右边界
const xLimit = head.x < 0 || head.x >= canvas.width
// 是否碰到上下边界
const yLimit = head.y < 0 || head.y >= canvas.height
// 是否撞到蛇身
const hitSelf = snake.body.find(({ x, y }) => head.x === x && head.y === y)
// 三者其中一个为true则游戏结束
return xLimit || yLimit || hitSelf
}
const snake = new Snake(3)
// 默认direction为2,也就是右
snake.direction = 2
snake.drawSnake()
// 创建随机食物实例
var food = randomFood(snake)
// 画出食物
food.draw()
function animate() {
// 先清空
ctx.clearRect(0, 0, canvas.width, canvas.height)
// 移动
snake.moveSnake()
// 再画
snake.drawSnake()
food.draw()
}
var timer = setInterval(() => {
animate()
}, 100)
}
结语
我是林三心,一个待过小型toG型外包公司、大型外包公司、小公司、潜力型创业公司、大公司的作死型前端选手