前端实现贪吃蛇小游戏-附源码详解

前端实现贪吃蛇小游戏的详细代码和解释。

首先,我们需要在 HTML 中添加一个画布元素,用于绘制游戏界面:

<canvas id="canvas" width="400" height="400">canvas>

然后,在 JavaScript 中,我们需要定义一些变量来表示游戏状态和参数:

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

const cellSize = 20
const width = canvas.width / cellSize
const height = canvas.height / cellSize

let snake = [{ x: 10, y: 10 }]
let direction = 'right'
let food = generateFood()

let score = 0
let gameOver = false

其中,canvasctx 分别表示画布元素和画布上下文。cellSize 表示每个格子的大小,widthheight 分别表示画布的宽度和高度。snake 表示蛇的身体,direction 表示蛇的移动方向,food 表示食物的位置。score 表示得分,gameOver 表示游戏是否结束。

接着,我们需要定义一些函数来实现游戏逻辑。首先是 generateFood 函数,用于生成随机食物的位置:

function generateFood () {
  let food = {
    x: Math.floor(Math.random() * width),
    y: Math.floor(Math.random() * height)
  }
  while (snake.some(cell => cell.x === food.x && cell.y === food.y)) {
    food = {
      x: Math.floor(Math.random() * width),
      y: Math.floor(Math.random() * height)
    }
  }
  return food
}

该函数首先随机生成一个食物的位置,然后检查该位置是否与蛇的身体重叠,如果是则重新生成位置,直到找到一个不与蛇重叠的位置。

接下来是 drawCell 函数,用于绘制一个格子:

function drawCell (x, y, color) {
  ctx.fillStyle = color
  ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize)
  ctx.strokeStyle = 'white'
  ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize)
}

该函数接受三个参数,分别表示格子的横坐标、纵坐标和颜色。它使用 fillRectstrokeRect 方法绘制格子的填充和边框。

接下来是 drawSnake 函数,用于绘制蛇的身体:

function drawSnake () {
  snake.forEach(cell => {
    drawCell(cell.x, cell.y, 'green')
  })
}

该函数使用 forEach 方法遍历蛇的身体,然后调用 drawCell 函数绘制每个格子。

接下来是 drawFood 函数,用于绘制食物:

function drawFood () {
  drawCell(food.x, food.y, 'red')
}

该函数调用 drawCell 函数绘制食物格子。

接下来是 update 函数,用于更新游戏状态:

function update () {
  const head = { x: snake[0].x, y: snake[0].y }
  switch (direction) {
    case 'up':
      head.y--
      break
    case 'down':
      head.y++
      break
    case 'left':
      head.x--
      break
    case 'right':
      head.x++
      break
  }
  if (head.x < 0 || head.x >= width || head.y < 0 || head.y >= height || snake.some(cell => cell.x === head.x && cell.y === head.y)) {
    gameOver = true
    return
  }
  snake.unshift(head)
  if (head.x === food.x && head.y === food.y) {
    score++
    food = generateFood()
  } else {
    snake.pop()
  }
}

该函数首先复制蛇头的位置,然后根据当前的移动方向更新蛇头的位置。如果蛇头超出了画布边界或者与蛇的身体重叠,则游戏结束。否则,将蛇头插入到蛇的身体的头部,然后检查是否吃到了食物。如果吃到了食物,则将得分加一,并生成新的食物。否则,将蛇的尾部删除。

最后是 draw 函数,用于绘制游戏界面:

function draw () {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  drawSnake()
  drawFood()
  ctx.fillStyle = 'white'
  ctx.fillText(`Score: ${score}`, 10, canvas.height - 10)
}

该函数首先使用 clearRect 方法清空画布,然后调用 drawSnakedrawFood 函数绘制蛇和食物,最后使用 fillText 方法绘制得分。

最后是游戏循环,使用 setInterval 方法每隔一段时间调用一次 updatedraw 函数:

setInterval(() => {
  if (!gameOver) {
    update()
    draw()
  }
}, 100)

至此,贪吃蛇小游戏的前端实现就完成了。完整代码如下:

好的,下面是完整代码和详细解释:

DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>贪吃蛇小游戏title>
    <style>
      canvas {
        border: 1px solid black;
      }
    style>
  head>
  <body>
    <canvas id="canvas" width="400" height="400">canvas>
    <script>
      const canvas = document.getElementById('canvas')
      const ctx = canvas.getContext('2d')

      const cellSize = 20
      const width = canvas.width / cellSize
      const height = canvas.height / cellSize

      let snake = [{ x: 10, y: 10 }]
      let direction = 'right'
      let food = generateFood()

      let score = 0
      let gameOver = false

      function generateFood () {
        let food = {
          x: Math.floor(Math.random() * width),
          y: Math.floor(Math.random() * height)
        }
        while (snake.some(cell => cell.x === food.x && cell.y === food.y)) {
          food = {
            x: Math.floor(Math.random() * width),
            y: Math.floor(Math.random() * height)
          }
        }
        return food
      }

      function drawCell (x, y, color) {
        ctx.fillStyle = color
        ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize)
        ctx.strokeStyle = 'white'
        ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize)
      }

      function drawSnake () {
        snake.forEach(cell => {
          drawCell(cell.x, cell.y, 'green')
        })
      }

      function drawFood () {
        drawCell(food.x, food.y, 'red')
      }

      function update () {
        const head = { x: snake[0].x, y: snake[0].y }
        switch (direction) {
          case 'up':
            head.y--
            break
          case 'down':
            head.y++
            break
          case 'left':
            head.x--
            break
          case 'right':
            head.x++
            break
        }
        if (head.x < 0 || head.x >= width || head.y < 0 || head.y >= height || snake.some(cell => cell.x === head.x && cell.y === head.y)) {
          gameOver = true
          return
        }
        snake.unshift(head)
        if (head.x === food.x && head.y === food.y) {
          score++
          food = generateFood()
        } else {
          snake.pop()
        }
      }

      function draw () {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        drawSnake()
        drawFood()
        ctx.fillStyle = 'white'
        ctx.fillText(`Score: ${score}`, 10, canvas.height - 10)
      }

      setInterval(() => {
        if (!gameOver) {
          update()
          draw()
        }
      }, 100)

      document.addEventListener('keydown', event => {
        switch (event.key) {
          case 'ArrowUp':
            if (direction !== 'down') {
              direction = 'up'
            }
            break
          case 'ArrowDown':
            if (direction !== 'up') {
              direction = 'down'
            }
            break
          case 'ArrowLeft':
            if (direction !== 'right') {
              direction = 'left'
            }
            break
          case 'ArrowRight':
            if (direction !== 'left') {
              direction = 'right'
            }
            break
        }
      })
    script>
  body>
html>

首先定义了一些常量,包括格子的大小、画布的宽度和高度等等。

然后定义了几个变量,包括蛇的初始位置、移动方向、食物的位置、得分和游戏是否结束等等。

接下来是 generateFood 函数,用于生成随机的食物位置:

function generateFood () {
  let food = {
    x: Math.floor(Math.random() * width),
    y: Math.floor(Math.random() * height)
  }
  while (snake.some(cell => cell.x === food.x && cell.y === food.y)) {
    food = {
      x: Math.floor(Math.random() * width),
      y: Math.floor(Math.random() * height)
    }
  }
  return food
}

该函数首先生成一个随机的食物位置,然后检查该位置是否与蛇的身体重叠。如果重叠,则重新生成随机位置,直到找到一个不与蛇的身体重叠的位置。

接下来是 drawCell 函数,用于绘制单个格子:

function drawCell (x, y, color) {
  ctx.fillStyle = color
  ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize)
  ctx.strokeStyle = 'white'
  ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize)
}

该函数接受三个参数,分别表示格子的横坐标、纵坐标和颜色。它使用 fillRectstrokeRect 方法绘制格子的填充和边框。

接下来是 drawSnake 函数,用于绘制蛇的身体:

function drawSnake () {
  snake.forEach(cell => {
    drawCell(cell.x, cell.y, 'green')
  })
}

该函数使用 forEach 方法遍历蛇的身体,然后调用 drawCell 函数绘制每个格子。

接下来是 drawFood 函数,用于绘制食物:

function drawFood () {
  drawCell(food.x, food.y, 'red')
}

该函数调用 drawCell 函数绘制食物格子。

接下来是 update 函数,用于更新游戏状态:

function update () {
  const head = { x: snake[0].x, y: snake[0].y }
  switch (direction) {
    case 'up':
      head.y--
      break
    case 'down':
      head.y++
      break
    case 'left':
      head.x--
      break
    case 'right':
      head.x++
      break
  }
  if (head.x < 0 || head.x >= width || head.y < 0 || head.y >= height || snake.some(cell => cell.x === head.x && cell.y === head.y)) {
    gameOver = true
    return
  }
  snake.unshift(head)
  if (head.x === food.x && head.y === food.y) {
    score++
    food = generateFood()
  } else {
    snake.pop()
  }
}

该函数首先创建一个新的头部位置,根据当前移动方向更新头部位置。然后检查头部是否超出边界或与蛇的身体重叠。如果是,则游戏结束。否则,将新的头部位置插入到蛇的身体前面。如果头部位置与食物位置重叠,则得分加一,生成新的食物位置,否则将蛇的尾部弹出,使其移动。

最后是 draw 函数,用于绘制游戏界面:

function draw () {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  drawSnake()
  drawFood()
  ctx.fillStyle = 'white'
  ctx.fillText(`Score: ${score}`, 10, canvas.height - 10)
}

该函数首先使用 clearRect 方法清空画布,然后分别调用 drawSnakedrawFood 函数绘制蛇和食物。最后使用 fillText 方法绘制得分。

最后是游戏主循环,使用 setInterval 方法每隔一定时间更新游戏状态并绘制游戏界面。并且监听键盘事件,根据按键更新移动方向。

setInterval(() => {
  if (!gameOver) {
    update()
    draw()
  }
}, 100)

document.addEventListener('keydown', event => {
  switch (event.key) {
    case 'ArrowUp':
      if (direction !== 'down') {
        direction = 'up'
      }
      break
    case 'ArrowDown':
      if (direction !== 'up') {
        direction = 'down'
      }
      break
    case 'ArrowLeft':
      if (direction !== 'right') {
        direction = 'left'
      }
      break
    case 'ArrowRight':
      if (direction !== 'left') {
        direction = 'right'
      }
      break
  }
})

这就是贪吃蛇游戏的全部代码。

你可能感兴趣的:(前端)