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

前言

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

创新互联公司是创新、创意、研发型一体的综合型网站建设公司,自成立以来公司不断探索创新,始终坚持为客户提供满意周到的服务,在本地打下了良好的口碑,在过去的十年时间我们累计服务了上千家以及全国政企客户,如成都地磅秤等企业单位,完善的项目管理流程,严格把控项目进度与质量监控加上过硬的技术实力获得客户的一致赞誉。

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()

CSS&HTML:

 



键盘上下左右控制,Enter键暂停




得分


0


游戏元素的话一共有 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()

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()
}

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
}
}
}
})

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)
}
}

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

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

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

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)
}
}

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()
}
}

写在最后

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

新闻名称:如何用原生 JS,快速写一个贪吃蛇小游戏
本文路径:http://www.shufengxianlan.com/qtweb/news6/65706.html

成都网站建设公司_创新互联,为您提供软件开发自适应网站网站建设建站公司网站排名关键词优化

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联