canvas实现贪食蛇的实践
发布时间:2022-02-14 16:40:07 作者:zzzzxcv
我要评论
本文主要介绍了canvas实现贪食蛇的实践,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
贪食蛇
最近 dan 有在油管上直播,播放量最多的就是手写一个贪食蛇。
本来想学一下大佬写代码的姿势,看了几分钟就没了耐性,心想我为什么不能自己写一个呢。
一步一步跟着敲代码,我实践了一段时间但是收效甚微,因为中间少了自己的思考。
初期可能有些作用,可以学到一些技巧和规范。
但是自己实现一个东西带来的成就感,你不断的 debug 和查文档查资料留下的记忆和习惯,这大概就是这个玩意带给我最大的收获吧。

我的 github 一直是单机模式,如果这篇文章对您有所帮助的话欢迎点个 star
数据结构及变量
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 解决
游戏规则中,蛇是不能反向移动的。
但是在事件回调中,如果改变方向过快,(5 次 loop 才执行一次重绘),就会出现方向变了,但是蛇的位置没变(比如蛇向右移动,我们先按上再按左),他就和自身碰撞了
解决方案:
我加了一个 canChangeDirection 变量,
当你改变方向之后,必须等待蛇移动了才能再次改变方向
// 事件回调 case "ArrowUp": if (direction === "down" |!canChangeDirection) return direction = "up" canChangeDirection = false break
到此这篇关于canvas实现贪食蛇的实践的文章就介绍到这了,更多相关 canvas贪食蛇内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!
相关文章
本文介绍HTML5实现移动端购物车自动结算,通过WebStorage、事件监听、DOM操作等技术,确保实时更新与数据同步,优化性能及无障碍性,提升用户体验,感兴趣的朋友一起看看吧2025-06-18- 在HTML5中,<button>标签用于定义一个可点击的按钮,它是创建交互式网页的重要元素之一,本文将深入解析HTML5中的<button>标签,详细介绍其属性、样式以及实际2025-06-18
基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)
本文将深入剖析一段基于 HTML5 Canvas 的代码,该代码实现了图片的旋转(90 度和 180 度)以及旋转后图片的下载功能,通过对代码的解读,我们可以学习到如何利用 Canvas API2025-06-18
HTML5 getUserMedia API网页录音实现指南示例小结
本教程将指导你如何利用这一API,结合Web Audio API,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步详解,此外,还讨论了getUserMedia API的使用限制和最2025-06-16- HTML5的搜索框是一个强大的工具,能够有效提升用户体验,通过结合自动补全功能和适当的样式,可以创建出既美观又实用的搜索界面,这篇文章给大家介绍HTML5 搜索框Search Box2025-06-13
- Checkbox是HTML5中非常重要的表单元素之一,通过合理使用其属性和样式自定义方法,可以为用户提供丰富多样的交互体验,这篇文章给大家介绍HTML5中Checkbox标签的深入全面解2025-06-13
- 本实例展示了一种基于HTML5技术的图片上传功能,无需外部插件即可通过拖放图片实现上传,涉及到HTML5的拖放API和File API,以及使用CSS来增强用户界面的交互性和视觉反馈,2025-05-16
- 在HTML5和CSS中,定位(positioning)是控制元素在页面上位置的重要机制,主要有四种定位方式:静态定位(static)、相对定位(relative)、绝对定位(absolute)和固定定位(fixed),2025-05-13
- Microdata作为HTML5新增的一个特性,它允许开发者在HTML文档中添加更多的语义信息,以便于搜索引擎和浏览器更好地理解页面内容,本文将探讨HTML5中Microdata的使用方法以及2025-04-21
在HTML语法中,表格主要通过< table >、< tr >和< td >3个标签构成,本文通过实例代码讲解HTML5表格语法格式,感兴趣的朋友一起看看吧2025-04-21





最新评论