贪食蛇
数据结构及变量
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贪食蛇内容请搜索代码网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持代码网!
发表评论