如何用原生 JS,快速写一个贪吃蛇小游戏

开发 前端
游戏元素的话一共有 3 个,蛇头,身体和苹果。就用 3 个构造函数来生成坐标,以及给对应坐标的那个对象里面添加不同的属性。

前言

贪吃蛇算是小游戏里面比较好写的,没有什么难点,基本上需要实现的功能,都能很顺利的用代码敲出来。

1、绘制游戏区域和游戏元素

仍然是用 16 * 16 的二维数组来绘制,对这个数组进行遍历。第一层遍历的时候创建 tr,第二层遍历的时候创建 td。然后添加一些 CSS 样式,游戏区域就写好了。

let arr = [  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
]
//渲染游戏区域
const renderTable = () {
  document.querySelector('table').innerHTML = ''
  arr.forEach(item {
    //第一层遍历创建tr
    let tr = document.createElement('tr')
    item.forEach(item2 {
      //第二层遍历创建td
      let td = document.createElement('td')
      tr.appendChild(td)
    })
    document.querySelector('table').appendChild(tr)
  })
}
renderTable()
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

CSS&HTML:

 <style>
    table {
      margin: 0px auto;
      border-collapse: collapse;
      border: 5px solid black;
    }
    td {
      border-radius: 50%;
      width: 40px;
      height: 40px;
      border: none
    }
    .bgc1 {
      background-color: black;
    }
    .bgc2 {
      background: url(./pngsucai_512252_f77802.png) no-repeat;
      background-size: 100%;
      z-index: 1;
    }
    .bgc3 {
      background: url(./92034446ddc7a91edec038f2d69415fd.jpeg)no-repeat;
      background-size: 100%;
      z-index: 9;
    }
    .defen {
      font-size: 30px;
      position: absolute;
      top: 40%;
      right: 20px;
    }
  </style>
  <body>
  <table></table>
  <div class="defen">
    键盘上下左右控制,Enter键暂停
    <br>
    <br>
    得分
    <br>
    <span>0</span>
  </div>
  <script src="./贪吃蛇2.0.js"></script>
</body>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.

游戏元素的话一共有 3 个,蛇头,身体和苹果。就用 3 个构造函数来生成坐标,以及给对应坐标的那个对象里面添加不同的属性。用构造函数写既方便查找,也方便修改。然后写个渲染函数,格子里面对应的不同的属性,就渲染出不同的样式。

//渲染样式的函数
const renderColor = () {
  arr.forEach((item, index) => {
    const trArr = document.querySelectorAll('tr')
    item.forEach((item2, index2) => {
      //头部渲染
      if (item2.head === 1) {
        trArr[index].querySelectorAll('td')[index2].classList.add('bgc3')
      }
      //身体渲染成黑色
      else if (item2.body === 1) {
        trArr[index].querySelectorAll('td')[index2].classList.remove('bgc3')
        trArr[index].querySelectorAll('td')[index2].classList.remove('bgc2')
        trArr[index].querySelectorAll('td')[index2].classList.add('bgc1')
      }
      //苹果渲染
      else if (item2.apple === 1) {
        trArr[index].querySelectorAll('td')[index2].classList.add('bgc2')
      }
      //如果是其他的值,就把这个样式清空
      else {
        trArr[index].querySelectorAll('td')[index2].className = ''
      }
    })
  })
}
//蛇头构造函数
function Head(x, y) {
  this.x = x
  this.y = y
  this.render = function (a) {
    arr[this.y][this.x].head = a
  }
}
//身体构造函数
function Body(x, y) {
  this.x = x
  this.y = y
  this.render = function (a) {
    arr[this.y][this.x].body = a
  }
}
//苹果构造函数
function Apple(x, y) {
  this.x = x
  this.y = y
  this.render = function (a) {
    arr[this.y][this.x].apple = a
  }
}
//依次渲染默认出现的头部,身体和苹果
let a = new Head(10, 10)
a.render(1)
//声明一个存放身体元素的数组,移动以及判断获胜都需要用到这个数组的功能
let bodyArr = []
let b = new Body(10, 11)
bodyArr.push(b)
b.render(1)
let c = new Apple(5, 5)
c.render(1)
renderColor()
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.

2、移动功能

移动功能要分两个步骤来写。一个是蛇头的移动,一个是身体的移动。贪吃蛇的身体它不是一个整理,是不能写成一块的。蛇头动的时候,身体它得扭来扭去,这才像个蛇。

蛇头移动很简单,上下左右键对应着蛇头 X 和 Y 两个值的加减。X 和 Y 超出范围,代码就会报错。就可以直接用 try catch 来判断边界。报错了就说明出界了,直接走 catch 的游戏结束。

注意: 这个游戏唯一麻烦一点的地方来了,怎么让蛇身体能扭起来。相通一个逻辑,这个问题就迎刃而解了。

蛇身体怎么移动,是身体里的每个元素都往前移动一格吗,显然不是。仔细观察你会发现,蛇移动时,身体的中间部分其实是不动的。动的只有最前端和最末端的两格。也就是说蛇移动时,其实就是把身体最末端的格子移动到了身体最前端,其他的都不需要动。前面声明的身体元素数组就是这个时候用的。把身体的最后一个元素移动到头部,同时数组里的最后一个元素也要移动到最前面去。

//上下左右移动函数
const up = () {
  //用try catch来判断是否出界
  try { //把移动的函数写在try里面,如果出界了就会报错,然后走catch里的代码
    //移动的时候先清除当前格子的样式
    a.render(0)
    a.y -= 1
    //然后渲染新样式
    a.render(1)
  } catch {
    clearInterval(timer)
    alert('游戏结束!')
    location.reload()
  }
  //调用吃苹果函数
  eat()
  //让数组中的最后一个元素,移动到头部刚才所在的位置
  bodyArr[bodyArr.length - 1].render(0)
  //这个a.x,a.y+1就是头部移动前的坐标
  bodyArr[bodyArr.length - 1].x = a.x
  bodyArr[bodyArr.length - 1].y = a.y + 1
  bodyArr[bodyArr.length - 1].render(1)
  //把身体数组里的最后一个元素移到最开头
  bodyArr.unshift(bodyArr.pop())
  renderColor()
  //调用判断游戏结束函数
  end()
}
const down = () {
  try {
    a.render(0)
    a.y += 1
    a.render(1)
  }
  catch {
    clearInterval(timer)
    alert('游戏结束!')
    location.reload()
  }
  eat()
  //让数组中的最后一个元素,移动到头部刚才所在的位置
  bodyArr[bodyArr.length - 1].render(0)
  bodyArr[bodyArr.length - 1].x = a.x
  bodyArr[bodyArr.length - 1].y = a.y - 1
  bodyArr[bodyArr.length - 1].render(1)
  bodyArr.unshift(bodyArr.pop())
  renderColor()
  end()
}
const right = () {
  try {
    a.render(0)
    a.x += 1
    a.render(1)
  }
  catch {
    clearInterval(timer)
    alert('游戏结束!')
    location.reload()
  }
  eat()
  //让数组中的最后一个元素,移动到头部刚才所在的位置
  bodyArr[bodyArr.length - 1].render(0)
  bodyArr[bodyArr.length - 1].x = a.x - 1
  bodyArr[bodyArr.length - 1].y = a.y
  bodyArr[bodyArr.length - 1].render(1)
  bodyArr.unshift(bodyArr.pop())
  renderColor()
  end()
}
const left = () {
  try {
    a.render(0)
    a.x -= 1
    a.render(1)
  }
  catch {
    clearInterval(timer)
    alert('游戏结束!')
    location.reload()
  }
  eat()
  //让数组中的最后一个元素,移动到头部刚才所在的位置
  bodyArr[bodyArr.length - 1].render(0)
  bodyArr[bodyArr.length - 1].x = a.x + 1
  bodyArr[bodyArr.length - 1].y = a.y
  bodyArr[bodyArr.length - 1].render(1)
  bodyArr.unshift(bodyArr.pop())
  renderColor()
  end()
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.

3、写键盘事件

写键盘事件的时候要加一个判断,让这个蛇只能够相对它自身左右移动。不能掉头,也不能向前冲,向前冲很容易就撞到墙了。

let num = 1
document.addEventListener('keydown', function (e) {
  //flag是暂停功能的变量
  if (flag) {
    if (e.key === 'ArrowDown') {
      //蛇头只能够向左或者向右移动,否则冲太快容易死。也不可以调头。
      if (num == 2 || num == 4) {
        down()
        num = 3
      }
    } else if (e.key === 'ArrowRight') {
      if (num == 1 || num == 3) {
        right()
        num = 2
      }
    } else if (e.key === 'ArrowLeft') {
      if (num == 1 || num == 3) {
        left()
        num = 4
      }
    } else if (e.key === 'ArrowUp') {
      if (num == 2 || num == 4) {
        up()
        num = 1
      }
    }
  }
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

4、吃苹果功能

吃苹果功能分为 3 个步骤

1.判断头部和苹果有没有重合,重合的话,就让这个苹果消失,让分数 +1。

2.生成随机坐标来渲染苹果,判断一下这个坐标上是否与蛇身体重合了,重合的话就要重新生成坐标。

3.生成一个新的身体实例,并且把这个新实例添加到身体数组中。

//得分
let fen = 0
//吃苹果
const eat = () {
  //如果头部和苹果重合了
  if (arr[a.y][a.x].apple == 1) {
    //清除这个苹果
    arr[a.y][a.x].apple = 0
    //分数加1
    fen++
    //调用判断游戏胜利函数
    win()
    //渲染分数
    document.querySelector('.defen span').innerHTML = fen
    //用循环来生成新苹果,满足条件就退出循环
    while (true) {
      const x = Math.floor(Math.random() * 16)
      const y = Math.floor(Math.random() * 16)
      //判断苹果不能出现在身体上
      if (!arr[y][x].head && !arr[y][x].body) {
        arr[y][x].apple = 1
        break
      }
    }
    //生成新的身体实例
    let b = new Body(bodyArr[bodyArr.length - 1].x, bodyArr[bodyArr.length - 1].y)
    b.render(1)
    bodyArr.push(b)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

5、头部碰到身体游戏失败的功能

和吃苹果的逻辑一样,就判断头部和身体是不是重合的。

//碰到身体游戏失败判定
const end = () {
  //如果头部和身体重合了
  if (arr[a.y][a.x].body == 1) {
    clearInterval(timer)
    alert('游戏结束!')
    location.reload(true)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

6、自动移动的功能

自动移动可以通过间歇函数来实现,然后不能单纯的在间歇函数的回调里面写上下左右的某一个,要判断一下蛇当前的移动方向是什么。然后来一个分数越高速度越快的功能。

//自动向前移动功能
let timer
//封装一个向前移动的函数
function move() {
  if (num == 1) {
    up()
  } else if (num == 2) {
    right()
  } else if (num == 3) {
    down()
  } else {
    left()
  }
}
//写自动移动的间歇函数
time()
function time() {
  //来个分越高速度越快的功能
  if (fen <= 20) {
    timer = setInterval(function () {
      move()
    }, 250)
  } else if (fen > 20 && fen <= 40) {
    clearInterval(timer)
    timer = setInterval(function () {
      move()
    }, 200)
  } else {
    clearInterval(timer)
    timer = setInterval(function () {
      move()
    }, 150)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

7、暂停功能和判断游戏胜利

这两个比较简单,就一起说了。暂停功能就是让定时器停止,并且让flag变量变为false。这样就不能再去控制蛇了。这个游戏一共有256个格子,胜利的条件就是身体有255个元素,因为要减去一个头部。

//暂停功能
let flag = true
document.addEventListener('keydown', function (e) {
  if (flag) {
    if (e.key === 'Enter') {
      clearInterval(timer)
      flag = !flag
    }
  } else {
    if (e.key === 'Enter') {
      time()
      flag = true
    }
  }
})
//胜利条件,身体数组的元素等于255个就获胜
const win = () {
  if (bodyArr.length == 255) {
    clearInterval(timer)
    alert('游戏胜利!')
    location.reload()
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

写在最后

游戏到这里就写完了,代码虽然看起来多,但是并没有什么难的地方,想到就能写。唯一麻烦一点的就是那个身体的移动,相通了就很简单了。

责任编辑:姜华 来源: 前端YUE
相关推荐

2020-08-20 20:30:49

C语言小游戏贪吃蛇

2022-11-09 11:57:17

原生JS五子棋

2021-06-15 09:18:51

鸿蒙HarmonyOS应用

2012-06-05 14:42:57

Silverlight

2024-12-17 17:46:05

Android原生控件贪吃蛇

2022-07-25 14:17:04

JS应用开发

2015-07-31 11:26:24

Swift贪吃蛇

2021-09-02 15:25:53

鸿蒙HarmonyOS应用

2024-01-18 11:22:41

C++Windows开发

2023-10-17 10:20:53

VueReact

2022-10-28 09:33:10

Linux贪吃蛇

2021-04-20 11:40:12

Linux图形库curses

2023-05-11 08:26:56

2021-04-13 06:35:13

Elixir语言编程语言软件开发

2016-09-22 21:12:14

2022-03-24 07:57:58

Python水果忍者游戏

2020-11-30 06:20:13

javascript

2016-09-14 21:17:47

PythonAsyncio游戏

2016-09-19 21:24:08

PythonAsyncio游戏

2025-02-27 09:31:05

点赞
收藏

51CTO技术栈公众号