canvas實(shí)現(xiàn)貪食蛇的實(shí)踐
發(fā)布時(shí)間:2022-02-14 16:40:07 作者:zzzzxcv
我要評(píng)論
本文主要介紹了canvas實(shí)現(xiàn)貪食蛇的實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
貪食蛇
最近 dan 有在油管上直播,播放量最多的就是手寫(xiě)一個(gè)貪食蛇。
本來(lái)想學(xué)一下大佬寫(xiě)代碼的姿勢(shì),看了幾分鐘就沒(méi)了耐性,心想我為什么不能自己寫(xiě)一個(gè)呢。
一步一步跟著敲代碼,我實(shí)踐了一段時(shí)間但是收效甚微,因?yàn)橹虚g少了自己的思考。
初期可能有些作用,可以學(xué)到一些技巧和規(guī)范。
但是自己實(shí)現(xiàn)一個(gè)東西帶來(lái)的成就感,你不斷的 debug 和查文檔查資料留下的記憶和習(xí)慣,這大概就是這個(gè)玩意帶給我最大的收獲吧。

我的 github 一直是單機(jī)模式,如果這篇文章對(duì)您有所幫助的話歡迎點(diǎn)個(gè) star
數(shù)據(jù)結(jié)構(gòu)及變量
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 繪制頁(yè)面
// 背景
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++) {
// 這里做了漸變色的蛇,添加動(dòng)態(tài)色彩。尾部有個(gè)最小白色閥值,免得跟背景混為一體
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
)
}
}
// 繪制食物
// 隨機(jī)生成食物的位置
function generateRandomFood() {
// 如果沒(méi)有位置可以生成
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
)
}
蛇的移動(dòng)
// 蛇的移動(dòng)
// 確定下一次移動(dòng)的位置,將這個(gè)點(diǎn)push到數(shù)組末尾(頭的位置),
// 將數(shù)組第一項(xiàng)shift出來(lái)(尾的位置)
// 吃食物的邏輯
// 如果食物的位置跟下一次移動(dòng)的位置相同,將這個(gè)點(diǎn)加入頭部,不推出尾部
function snakeMove() {
let next
let last = snake[snake.length - 1]
// 根據(jù)方向確定下一個(gè)蛇頭的位置
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)
// 碰撞重新開(kāi)始游戲
if (boundary || selfCollision) {
return restart()
}
snake.push(next)
// 如果下一個(gè)點(diǎn)是食物的位置,不推出頭部
if (next[0] === foodPosition[0] && next[1] === foodPosition[1]) {
generateRandomFood()
return
}
snake.shift()
canChangeDirection = true
}
事件監(jiān)聽(tīng)
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 實(shí)現(xiàn)動(dòng)畫(huà)
// 默認(rèn)的requestAnimationFrame循環(huán)應(yīng)該是60幀,對(duì)于這個(gè)游戲來(lái)說(shuō)太快了。
// 所以做了限制,5次loop才渲染(蛇移動(dòng)一格)一次
function animate() {
let count = 0
function loop() {
if (++count > 5) {
draw()
count = 0
}
requestAnimationFrame(loop)
}
requestAnimationFrame(loop)
}
BUG 解決
游戲規(guī)則中,蛇是不能反向移動(dòng)的。
但是在事件回調(diào)中,如果改變方向過(guò)快,(5 次 loop 才執(zhí)行一次重繪),就會(huì)出現(xiàn)方向變了,但是蛇的位置沒(méi)變(比如蛇向右移動(dòng),我們先按上再按左),他就和自身碰撞了
解決方案:
我加了一個(gè) canChangeDirection 變量,
當(dāng)你改變方向之后,必須等待蛇移動(dòng)了才能再次改變方向
// 事件回調(diào) case "ArrowUp": if (direction === "down" |!canChangeDirection) return direction = "up" canChangeDirection = false break
到此這篇關(guān)于canvas實(shí)現(xiàn)貪食蛇的實(shí)踐的文章就介紹到這了,更多相關(guān) canvas貪食蛇內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章

HTML5實(shí)現(xiàn)的移動(dòng)端購(gòu)物車自動(dòng)結(jié)算功能示例代碼
本文介紹HTML5實(shí)現(xiàn)移動(dòng)端購(gòu)物車自動(dòng)結(jié)算,通過(guò)WebStorage、事件監(jiān)聽(tīng)、DOM操作等技術(shù),確保實(shí)時(shí)更新與數(shù)據(jù)同步,優(yōu)化性能及無(wú)障礙性,提升用戶體驗(yàn),感興趣的朋友一起看看吧2025-06-18- 在HTML5中,<button>標(biāo)簽用于定義一個(gè)可點(diǎn)擊的按鈕,它是創(chuàng)建交互式網(wǎng)頁(yè)的重要元素之一,本文將深入解析HTML5中的<button>標(biāo)簽,詳細(xì)介紹其屬性、樣式以及實(shí)際2025-06-18
基于 HTML5 Canvas 實(shí)現(xiàn)圖片旋轉(zhuǎn)與下載功能(完整代碼展示)
本文將深入剖析一段基于 HTML5 Canvas 的代碼,該代碼實(shí)現(xiàn)了圖片的旋轉(zhuǎn)(90 度和 180 度)以及旋轉(zhuǎn)后圖片的下載功能,通過(guò)對(duì)代碼的解讀,我們可以學(xué)習(xí)到如何利用 Canvas API2025-06-18
HTML5 getUserMedia API網(wǎng)頁(yè)錄音實(shí)現(xiàn)指南示例小結(jié)
本教程將指導(dǎo)你如何利用這一API,結(jié)合Web Audio API,實(shí)現(xiàn)網(wǎng)頁(yè)錄音功能,從獲取音頻流到處理和保存錄音,整個(gè)過(guò)程將逐步詳解,此外,還討論了getUserMedia API的使用限制和最2025-06-16- HTML5的搜索框是一個(gè)強(qiáng)大的工具,能夠有效提升用戶體驗(yàn),通過(guò)結(jié)合自動(dòng)補(bǔ)全功能和適當(dāng)?shù)臉邮?,可以?chuàng)建出既美觀又實(shí)用的搜索界面,這篇文章給大家介紹HTML5 搜索框Search Box2025-06-13
- Checkbox是HTML5中非常重要的表單元素之一,通過(guò)合理使用其屬性和樣式自定義方法,可以為用戶提供豐富多樣的交互體驗(yàn),這篇文章給大家介紹HTML5中Checkbox標(biāo)簽的深入全面解2025-06-13
HTML5無(wú)插件拖拽圖片上傳功能實(shí)現(xiàn)過(guò)程
本實(shí)例展示了一種基于HTML5技術(shù)的圖片上傳功能,無(wú)需外部插件即可通過(guò)拖放圖片實(shí)現(xiàn)上傳,涉及到HTML5的拖放API和File API,以及使用CSS來(lái)增強(qiáng)用戶界面的交互性和視覺(jué)反饋,2025-05-16HTML5 定位大全之相對(duì)定位、絕對(duì)定位和固定定位
在HTML5和CSS中,定位(positioning)是控制元素在頁(yè)面上位置的重要機(jī)制,主要有四種定位方式:靜態(tài)定位(static)、相對(duì)定位(relative)、絕對(duì)定位(absolute)和固定定位(fixed),2025-05-13- Microdata作為HTML5新增的一個(gè)特性,它允許開(kāi)發(fā)者在HTML文檔中添加更多的語(yǔ)義信息,以便于搜索引擎和瀏覽器更好地理解頁(yè)面內(nèi)容,本文將探討HTML5中Microdata的使用方法以及2025-04-21
在HTML語(yǔ)法中,表格主要通過(guò)< table >、< tr >和< td >3個(gè)標(biāo)簽構(gòu)成,本文通過(guò)實(shí)例代碼講解HTML5表格語(yǔ)法格式,感興趣的朋友一起看看吧2025-04-21




