最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)物車(chē)自動(dòng)結(jié)算功能示例代碼

    本文介紹HTML5實(shí)現(xiàn)移動(dòng)端購(gòu)物車(chē)自動(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)簽用法和特征

    在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 API
    2025-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 搜索框Search Box詳解

    HTML5的搜索框是一個(gè)強(qiáng)大的工具,能夠有效提升用戶體驗(yàn),通過(guò)結(jié)合自動(dòng)補(bǔ)全功能和適當(dāng)?shù)臉邮剑梢詣?chuàng)建出既美觀又實(shí)用的搜索界面,這篇文章給大家介紹HTML5 搜索框Search Box
    2025-06-13
  • 全面解析HTML5中Checkbox標(biāo)簽

    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-16
  • HTML5 定位大全之相對(duì)定位、絕對(duì)定位和固定定位

    在HTML5和CSS中,定位(positioning)是控制元素在頁(yè)面上位置的重要機(jī)制,主要有四種定位方式:靜態(tài)定位(static)、相對(duì)定位(relative)、絕對(duì)定位(absolute)和固定定位(fixed),
    2025-05-13
  • HTML5中的Microdata與歷史記錄管理詳解

    Microdata作為HTML5新增的一個(gè)特性,它允許開(kāi)發(fā)者在HTML文檔中添加更多的語(yǔ)義信息,以便于搜索引擎和瀏覽器更好地理解頁(yè)面內(nèi)容,本文將探討HTML5中Microdata的使用方法以及
    2025-04-21
  • HTML5表格語(yǔ)法格式詳解

    在HTML語(yǔ)法中,表格主要通過(guò)< table >、< tr >和< td >3個(gè)標(biāo)簽構(gòu)成,本文通過(guò)實(shí)例代碼講解HTML5表格語(yǔ)法格式,感興趣的朋友一起看看吧
    2025-04-21

最新評(píng)論

高雄市| 横山县| 嘉禾县| 白山市| 华蓥市| 塔河县| 镇坪县| 本溪市| 连山| 刚察县| 丰镇市| 嘉禾县| 淮滨县| 石狮市| 四平市| 东平县| 昌平区| 清远市| 扎兰屯市| 潮州市| 鹤庆县| 商都县| 吉首市| 岳池县| 武威市| 固安县| 枣强县| 东阳市| 吉木萨尔县| 石台县| 牙克石市| 双鸭山市| 怀化市| 松溪县| 静宁县| 张北县| 巴中市| 巩义市| 全南县| 临漳县| 封开县|