贪食蛇
数据结构及变量
const canvas = document.getelementbyid("canvas")
const ctx = canvas.getcontext("2d")
const width = 400
const height = 400
const celllength = 20
let foodposition
let initsnake = [
[0, 0],
[1, 0],
[2, 0],
]
let snake = [...initsnake]
let direction = "right"
let canchangedirection = true
canvas 绘制页面
// 背景
function drawbackground() {
ctx.strokestyle = "#bfbfbf"
for (let i = 0; i <= height / celllength; i++) {
ctx.beginpath()
ctx.moveto(0, celllength * i)
ctx.lineto(width, celllength * i)
ctx.stroke()
}
for (let i = 0; i <= width / celllength; i++) {
ctx.beginpath()
ctx.moveto(celllength * i, 0)
ctx.lineto(celllength * i, height)
ctx.stroke()
}
}
// 蛇
function drawsnake() {
let step = 100 / (snake.length - 1)
for (let i = 0; i < snake.length; i++) {
// 这里做了渐变色的蛇,添加动态色彩。尾部有个最小白色阀值,免得跟背景混为一体
const percent = math.min(100 - step * i, 90)
ctx.fillstyle = `hsl(0,0%,${percent}%)`
ctx.fillrect(
snake[i][0] * celllength,
snake[i][1] * celllength,
celllength,
celllength
)
}
}
// 绘制食物
// 随机生成食物的位置
function generaterandomfood() {
// 如果没有位置可以生成
if (snake.length > width * height) {
return alert("you win")
}
const randomx = math.floor(math.random() * (width / celllength))
const randomy = math.floor(math.random() * (height / celllength))
// 生成的位置如果跟蛇体积碰撞,则重新生成。
for (let i = 0; i < snake.length; i++) {
if (snake[i][0] === randomx && snake[i][1] === randomy) {
return generaterandomfood()
}
}
foodposition = [randomx, randomy]
}
// 绘制
function drawfood() {
ctx.fillstyle = "#ff7875"
ctx.fillrect(
foodposition[0] * celllength,
foodposition[1] * celllength,
celllength,
celllength
)
}
蛇的移动
// 蛇的移动
// 确定下一次移动的位置,将这个点push到数组末尾(头的位置),
// 将数组第一项shift出来(尾的位置)
// 吃食物的逻辑
// 如果食物的位置跟下一次移动的位置相同,将这个点加入头部,不推出尾部
function snakemove() {
let next
let last = snake[snake.length - 1]
// 根据方向确定下一个蛇头的位置
switch (direction) {
case "up": {
next = [last[0], last[1] - 1]
break
}
case "down": {
next = [last[0], last[1] + 1]
break
}
case "left": {
next = [last[0] - 1, last[1]]
break
}
case "right": {
next = [last[0] + 1, last[1]]
break
}
}
// 边缘碰撞
const boundary =
next[0] < 0 ||
next[0] >= width / celllength ||
next[1] < 0 ||
next[1] >= height / celllength
// 自身碰撞
const selfcollision = snake.some(([x, y]) => next[0] === x && next[1] === y)
// 碰撞重新开始游戏
if (boundary || selfcollision) {
return restart()
}
snake.push(next)
// 如果下一个点是食物的位置,不推出头部
if (next[0] === foodposition[0] && next[1] === foodposition[1]) {
generaterandomfood()
return
}
snake.shift()
canchangedirection = true
}
事件监听
document.addeventlistener("keydown", (e) => {
switch (e.key) {
case "arrowup":
if (direction === "down" || !canchangedirection) return
direction = "up"
canchangedirection = false
break
case "arrowdown":
if (direction === "up" || !canchangedirection) return
direction = "down"
canchangedirection = false
break
case "arrowleft":
if (direction === "right" || !canchangedirection) return
direction = "left"
canchangedirection = false
break
case "arrowright":
if (direction === "left" || !canchangedirection) return
direction = "right"
canchangedirection = false
break
}
})
requestanimationframe 实现动画
// 默认的requestanimationframe循环应该是60帧,对于这个游戏来说太快了。
// 所以做了限制,5次loop才渲染(蛇移动一格)一次
function animate() {
let count = 0
function loop() {
if (++count > 5) {
draw()
count = 0
}
requestanimationframe(loop)
}
requestanimationframe(loop)
}
bug 解决
// 事件回调 case "arrowup": if (direction === "down" |!canchangedirection) return direction = "up" canchangedirection = false break
到此这篇关于canvas实现贪食蛇的实践的文章就介绍到这了,更多相关 canvas贪食蛇内容请搜索代码网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持代码网!
发表评论