JS实现贪吃蛇

上周突然想做个小游戏,想起2016年年底做过一个2048体验一把GitHub:https://github.com/YutLee/game-2048

现在就做一个简单的贪吃蛇,来一局GitHub:https://github.com/YutLee/snake好了,于是某天下午构思并完成了第一个版本,前天晚上想起还有bug,又花点时间修复

5BEA6381-87E4-46F3-86BD-79E0C90876FD.GIF

下面说说制作思路:


QQ截图20210714105702.png

整个游戏有什么呢,首先是场地、蛇、食物,然后可以控制蛇转向。
第一步:生成场地,场地我们用是一个10*10的二维数组坐标来标记

/**
 * 生成场地
 */
let maxX = 10, maxY = 10
function createSpace () {
  var space = []

  for (var i = 0; i < maxY; i++) {
    var xs = []
    for (var j = 0; j < maxX; j++) {
      xs.push([j, i])
    }
    space.push(xs)
  }
  return space
}

蛇和食物都是坐标上的点,那么我们需要一个方法生成一个随机整数

/**
 * 生成一个随机整数
 */
function createInt (max) {
  return Math.floor(Math.random() * max)
}

然后需要一个在场地范围内生成随机点的方法

/**
 * 在场地内生成随机点
 */
function createPoint (existed = []) {
  let empty, idx = 0

  empty = emptySpace.filter(item => existed.find(entry => entry[0] === item[0] && entry[1] === item[1]) === undefined)
  idx = createInt(empty.length)

  return empty[idx]
}

第二步:生成蛇,为了一开始可以区分蛇和食物,蛇需要用相连的两个点来表示,所以生成一个随机点代表蛇头,然后在该点上下左右依次判断是否在场地内,如果是则该点视为蛇尾

/**
 * 生成蛇
 */
let snakes = []
function createSnake () {
  const snake = createPoint()
 
  snakes.push(snake)

  if (snake[0] + 1 < maxX) {
    snakes.push([snake[0] + 1, snake[1]])
  } else if (snake[0] - 1 < maxX) {
    snakes.push([snake[0] - 1, snake[1]])
  } else if (snake[1] + 1 < maxY) {
    snakes.push([snake[0], snake[1] + 1])
  } else if (snake[1] - 1 < maxY) {
    snakes.push([snake[0], snake[1] - 1])
  }
}

第三步:生成食物

/**
 * 生成食物
 */
let food
function createFood (snakes) {
  food = createPoint(snakes)
}

第四步:对边界的判断,墙壁、蛇身、没有空间了(赢了)

/**
 * 判断是否墙壁
 */
function isWall (x, y) {
  return x < 0 || y < 0 || x >= maxX || y >= maxY
}

/**
 * 判断是否咬到自己
 */
function isSelf (x, y) {
  const size = snakes.length
  let self = false

  for (var i = 4; i < size; i++) {
    if (snakes[i][0] === x && snakes[i][1] === y) {
      self = true
      break
    }
  }

  return self
}

/**
 * 判断是否赢了(没有空间了)
 */
const maxSize = maxX * maxY
function isWin () {
  return snakes.length === maxSize
}

/**
 * 游戏结束
 */
let isOver
function gameOver (text) {
  isOver = true
  clearTimeout(timer)
  clearEvent(text)
}

第五步:实体都准备好了,那么需要蛇可以移动

/**
 * 获取下一步坐标
 */
function nextPoint (point = [], step = 1) {
  const steps = getStep(step)[directionIdx]
  return {
    x: point[0] + steps[0], 
    y: point[1] + steps[1]
  }
}

/**
 * 蛇移动
 */
let timer 
const speed = 500 // 每500ms前进一步
function move () {
  const { x, y } = nextPoint(snakes[0])
  let last

  clearTimeout(timer)

  if (isOver) return

  if (isWall(x, y)) {
    gameOver('蛇撞墙而亡')
    return
  }

  if (isSelf(x, y)) {
    gameOver('蛇吃了自己')
    return
  }

  if (isWin()) {
    gameOver('蛇吃饱了')
    return
  }

  snakes.unshift([x, y])

  if (food[0] === x && food[1] === y) {
    score += 10
    createFood(snakes)
  } else {
    last = snakes.pop()
  }

  // snakeMove(last) //界面上移动效果

  timer = setTimeout(() => {
    move()
  }, speed)
}

第六步:上面都是数据,那么还需要在界面上显示,加入界面更新方法

/**
 * 创建空间UI
 */
function createSpaceUI () {
  const space = createSpace()
  const dom = document.getElementById('space')
  let html = ''

  space.forEach(item => {
    html += `<div class="row flex">`
    item.forEach(entry => {
      emptySpace.push(entry)
      html += `<div class="column" id="${entry[0]}-${entry[1]}"></div>`
    })
    html += '</div>'
  })

  dom.innerHTML = html
}

/**
 * 获取移动步伐
 */
function getStep (step = 1) {
  return [[-step, 0], [0, -step], [step, 0], [0, step]]
}

/**
 * 蛇移动UI
 */
function snakeMove (last = []) {
  let lastDom

  if (Array.isArray(last)) {
    lastDom = document.getElementById(`${last[0]}-${last[1]}`)

    if (lastDom) {
      lastDom.className = 'column'
    }
  }

  snakes.forEach(item => {
    const snakesDom = document.getElementById(`${item[0]}-${item[1]}`)

    if (snakesDom) {
      snakesDom.className = 'column snake'
    }
  })
}

第七步:加入事件,键盘上下左右键,WSAD键和界面按钮事件

window.addEventListener('keydown', handleKeyDown, false)

document.getElementById('handle').addEventListener('click', handleClick, false)

function clearEvent (text) {
  document.getElementById('alert-title').innerHTML = text
  document.getElementById('this-score').innerHTML = score
  document.getElementById('shadow').className = 'shadow open'
  document.removeEventListener('keydown', handleKeyDown, false)
}

function handleClick (event) {
  const id = event.target.id
  const keyCode = Number(id.replace('key-', ''))

  if (id === 'handle') return

  handleMove(keyCode)
}

function handleKeyDown (event) {
  let keyCode = event.which || event.keyCode

  if (keyCode === 65) { // A
    keyCode = 37
  } else if (keyCode === 87) { // W
    keyCode = 38
  } else if (keyCode === 68) { // D
    keyCode = 39
  } else if (keyCode === 83) { // S
    keyCode = 40
  }

  handleMove(keyCode)
}

function handleMove (keyCode) {
  const currentIdx = keyCode - 37
  const isForward = directionIdx === currentIdx
  const isBack = directionIdx === currentIdx + (keyCode > 38 ? -2 : 2)
  const isTurn = !isForward && !isBack && /^(37|38|39|40)$/.test(keyCode)
  const pauseDom = document.getElementById('key-32')

  if (keyCode === 32) { // 空格
    if (isPause === true) {
      isPause = false
      move()
      pauseDom.innerHTML = '暂时'
    } else {
      isPause = true
      clearTimeout(timer)
      pauseDom.innerHTML = '开始'
    }
    return
  }

  if (isForward) {
    clearTimeout(timer)
    move()
  }

  if (isTurn) {
    clearTimeout(timer)
    directionIdx = currentIdx
    direction = directions[directionIdx]
    move()
  }
}

OK,游戏开始

/**
 * 游戏开始
 */
function start () {
  createSpaceUI()
  createSnake()
  createFood(snakes)
  snakeMove()
  move()
}

完整代码请查看https://github.com/YutLee/snake

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 思路: 1.创建画布 2.创建的蛇头、身体、食物 3.蛇头、身体、食物随机产生坐标 4.设置蛇头的初始方向 5.设...
    GY_3ade阅读 1,075评论 0 0
  • 先上图: 废话不多说,直接奉上代码 <!DOCTYPEhtml> *{ margin:0; padding:0; ...
    小样a阅读 419评论 0 0
  • 1. 游戏设计与数据设计 感谢您选择pyera,如果有任何问题想反馈和讨论可以加群:py/era 技术吹水群 54...
    氰酸钾铝阅读 2,576评论 3 3
  • 这篇文章主要介绍了python实现贪吃蛇小游戏,由键盘控制snake方向,吃食物功能。游戏基于pygame框架制作...
    极客匠阅读 700评论 0 1
  • 最近在家玩了贪吃蛇大作战,突发兴致下就想到用JS来写一个贪吃蛇的简单Dome,这里并没有用canvas来编写,有空...
    _Gary阅读 813评论 4 4