新聞中心
前言
貪吃蛇算是小游戲里面比較好寫(xiě)的,沒(méi)有什么難點(diǎn),基本上需要實(shí)現(xiàn)的功能,都能很順利的用代碼敲出來(lái)。

創(chuàng)新互聯(lián)公司是創(chuàng)新、創(chuàng)意、研發(fā)型一體的綜合型網(wǎng)站建設(shè)公司,自成立以來(lái)公司不斷探索創(chuàng)新,始終堅(jiān)持為客戶提供滿意周到的服務(wù),在本地打下了良好的口碑,在過(guò)去的十年時(shí)間我們累計(jì)服務(wù)了上千家以及全國(guó)政企客戶,如成都地磅秤等企業(yè)單位,完善的項(xiàng)目管理流程,嚴(yán)格把控項(xiàng)目進(jìn)度與質(zhì)量監(jiān)控加上過(guò)硬的技術(shù)實(shí)力獲得客戶的一致贊譽(yù)。
1、繪制游戲區(qū)域和游戲元素
仍然是用 16 * 16 的二維數(shù)組來(lái)繪制,對(duì)這個(gè)數(shù)組進(jìn)行遍歷。第一層遍歷的時(shí)候創(chuàng)建 tr,第二層遍歷的時(shí)候創(chuàng)建 td。然后添加一些 CSS 樣式,游戲區(qū)域就寫(xiě)好了。
let arr = [ [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
]
//渲染游戲區(qū)域
const renderTable = () {
document.querySelector('table').innerHTML = ''
arr.forEach(item {
//第一層遍歷創(chuàng)建tr
let tr = document.createElement('tr')
item.forEach(item2 {
//第二層遍歷創(chuàng)建td
let td = document.createElement('td')
tr.appendChild(td)
})
document.querySelector('table').appendChild(tr)
})
}
renderTable()CSS&HTML:
鍵盤(pán)上下左右控制,Enter鍵暫停
得分
0
游戲元素的話一共有 3 個(gè),蛇頭,身體和蘋(píng)果。就用 3 個(gè)構(gòu)造函數(shù)來(lái)生成坐標(biāo),以及給對(duì)應(yīng)坐標(biāo)的那個(gè)對(duì)象里面添加不同的屬性。用構(gòu)造函數(shù)寫(xiě)既方便查找,也方便修改。然后寫(xiě)個(gè)渲染函數(shù),格子里面對(duì)應(yīng)的不同的屬性,就渲染出不同的樣式。
//渲染樣式的函數(shù)
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')
}
//蘋(píng)果渲染
else if (item2.apple === 1) {
trArr[index].querySelectorAll('td')[index2].classList.add('bgc2')
}
//如果是其他的值,就把這個(gè)樣式清空
else {
trArr[index].querySelectorAll('td')[index2].className = ''
}
})
})
}
//蛇頭構(gòu)造函數(shù)
function Head(x, y) {
this.x = x
this.y = y
this.render = function (a) {
arr[this.y][this.x].head = a
}
}
//身體構(gòu)造函數(shù)
function Body(x, y) {
this.x = x
this.y = y
this.render = function (a) {
arr[this.y][this.x].body = a
}
}
//蘋(píng)果構(gòu)造函數(shù)
function Apple(x, y) {
this.x = x
this.y = y
this.render = function (a) {
arr[this.y][this.x].apple = a
}
}
//依次渲染默認(rèn)出現(xiàn)的頭部,身體和蘋(píng)果
let a = new Head(10, 10)
a.render(1)
//聲明一個(gè)存放身體元素的數(shù)組,移動(dòng)以及判斷獲勝都需要用到這個(gè)數(shù)組的功能
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、移動(dòng)功能
移動(dòng)功能要分兩個(gè)步驟來(lái)寫(xiě)。一個(gè)是蛇頭的移動(dòng),一個(gè)是身體的移動(dòng)。貪吃蛇的身體它不是一個(gè)整理,是不能寫(xiě)成一塊的。蛇頭動(dòng)的時(shí)候,身體它得扭來(lái)扭去,這才像個(gè)蛇。
蛇頭移動(dòng)很簡(jiǎn)單,上下左右鍵對(duì)應(yīng)著蛇頭 X 和 Y 兩個(gè)值的加減。X 和 Y 超出范圍,代碼就會(huì)報(bào)錯(cuò)。就可以直接用 try catch 來(lái)判斷邊界。報(bào)錯(cuò)了就說(shuō)明出界了,直接走 catch 的游戲結(jié)束。
注意: 這個(gè)游戲唯一麻煩一點(diǎn)的地方來(lái)了,怎么讓蛇身體能扭起來(lái)。相通一個(gè)邏輯,這個(gè)問(wèn)題就迎刃而解了。
蛇身體怎么移動(dòng),是身體里的每個(gè)元素都往前移動(dòng)一格嗎,顯然不是。仔細(xì)觀察你會(huì)發(fā)現(xiàn),蛇移動(dòng)時(shí),身體的中間部分其實(shí)是不動(dòng)的。動(dòng)的只有最前端和最末端的兩格。也就是說(shuō)蛇移動(dòng)時(shí),其實(shí)就是把身體最末端的格子移動(dòng)到了身體最前端,其他的都不需要?jiǎng)印G懊媛暶鞯纳眢w元素?cái)?shù)組就是這個(gè)時(shí)候用的。把身體的最后一個(gè)元素移動(dòng)到頭部,同時(shí)數(shù)組里的最后一個(gè)元素也要移動(dòng)到最前面去。
//上下左右移動(dòng)函數(shù)
const up = () {
//用try catch來(lái)判斷是否出界
try { //把移動(dòng)的函數(shù)寫(xiě)在try里面,如果出界了就會(huì)報(bào)錯(cuò),然后走catch里的代碼
//移動(dòng)的時(shí)候先清除當(dāng)前格子的樣式
a.render(0)
a.y -= 1
//然后渲染新樣式
a.render(1)
} catch {
clearInterval(timer)
alert('游戲結(jié)束!')
location.reload()
}
//調(diào)用吃蘋(píng)果函數(shù)
eat()
//讓數(shù)組中的最后一個(gè)元素,移動(dòng)到頭部剛才所在的位置
bodyArr[bodyArr.length - 1].render(0)
//這個(gè)a.x,a.y+1就是頭部移動(dòng)前的坐標(biāo)
bodyArr[bodyArr.length - 1].x = a.x
bodyArr[bodyArr.length - 1].y = a.y + 1
bodyArr[bodyArr.length - 1].render(1)
//把身體數(shù)組里的最后一個(gè)元素移到最開(kāi)頭
bodyArr.unshift(bodyArr.pop())
renderColor()
//調(diào)用判斷游戲結(jié)束函數(shù)
end()
}
const down = () {
try {
a.render(0)
a.y += 1
a.render(1)
}
catch {
clearInterval(timer)
alert('游戲結(jié)束!')
location.reload()
}
eat()
//讓數(shù)組中的最后一個(gè)元素,移動(dòng)到頭部剛才所在的位置
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('游戲結(jié)束!')
location.reload()
}
eat()
//讓數(shù)組中的最后一個(gè)元素,移動(dòng)到頭部剛才所在的位置
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('游戲結(jié)束!')
location.reload()
}
eat()
//讓數(shù)組中的最后一個(gè)元素,移動(dòng)到頭部剛才所在的位置
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、寫(xiě)鍵盤(pán)事件
寫(xiě)鍵盤(pán)事件的時(shí)候要加一個(gè)判斷,讓這個(gè)蛇只能夠相對(duì)它自身左右移動(dòng)。不能掉頭,也不能向前沖,向前沖很容易就撞到墻了。
let num = 1
document.addEventListener('keydown', function (e) {
//flag是暫停功能的變量
if (flag) {
if (e.key === 'ArrowDown') {
//蛇頭只能夠向左或者向右移動(dòng),否則沖太快容易死。也不可以調(diào)頭。
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、吃蘋(píng)果功能
吃蘋(píng)果功能分為 3 個(gè)步驟
1.判斷頭部和蘋(píng)果有沒(méi)有重合,重合的話,就讓這個(gè)蘋(píng)果消失,讓分?jǐn)?shù) +1。
2.生成隨機(jī)坐標(biāo)來(lái)渲染蘋(píng)果,判斷一下這個(gè)坐標(biāo)上是否與蛇身體重合了,重合的話就要重新生成坐標(biāo)。
3.生成一個(gè)新的身體實(shí)例,并且把這個(gè)新實(shí)例添加到身體數(shù)組中。
//得分
let fen = 0
//吃蘋(píng)果
const eat = () {
//如果頭部和蘋(píng)果重合了
if (arr[a.y][a.x].apple == 1) {
//清除這個(gè)蘋(píng)果
arr[a.y][a.x].apple = 0
//分?jǐn)?shù)加1
fen++
//調(diào)用判斷游戲勝利函數(shù)
win()
//渲染分?jǐn)?shù)
document.querySelector('.defen span').innerHTML = fen
//用循環(huán)來(lái)生成新蘋(píng)果,滿足條件就退出循環(huán)
while (true) {
const x = Math.floor(Math.random() * 16)
const y = Math.floor(Math.random() * 16)
//判斷蘋(píng)果不能出現(xiàn)在身體上
if (!arr[y][x].head && !arr[y][x].body) {
arr[y][x].apple = 1
break
}
}
//生成新的身體實(shí)例
let b = new Body(bodyArr[bodyArr.length - 1].x, bodyArr[bodyArr.length - 1].y)
b.render(1)
bodyArr.push(b)
}
}
5、頭部碰到身體游戲失敗的功能
和吃蘋(píng)果的邏輯一樣,就判斷頭部和身體是不是重合的。
//碰到身體游戲失敗判定
const end = () {
//如果頭部和身體重合了
if (arr[a.y][a.x].body == 1) {
clearInterval(timer)
alert('游戲結(jié)束!')
location.reload(true)
}
}
6、自動(dòng)移動(dòng)的功能
自動(dòng)移動(dòng)可以通過(guò)間歇函數(shù)來(lái)實(shí)現(xiàn),然后不能單純的在間歇函數(shù)的回調(diào)里面寫(xiě)上下左右的某一個(gè),要判斷一下蛇當(dāng)前的移動(dòng)方向是什么。然后來(lái)一個(gè)分?jǐn)?shù)越高速度越快的功能。
//自動(dòng)向前移動(dòng)功能
let timer
//封裝一個(gè)向前移動(dòng)的函數(shù)
function move() {
if (num == 1) {
up()
} else if (num == 2) {
right()
} else if (num == 3) {
down()
} else {
left()
}
}
//寫(xiě)自動(dòng)移動(dòng)的間歇函數(shù)
time()
function time() {
//來(lái)個(gè)分越高速度越快的功能
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、暫停功能和判斷游戲勝利
這兩個(gè)比較簡(jiǎn)單,就一起說(shuō)了。暫停功能就是讓定時(shí)器停止,并且讓flag變量變?yōu)閒alse。這樣就不能再去控制蛇了。這個(gè)游戲一共有256個(gè)格子,勝利的條件就是身體有255個(gè)元素,因?yàn)橐獪p去一個(gè)頭部。
//暫停功能
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
}
}
})
//勝利條件,身體數(shù)組的元素等于255個(gè)就獲勝
const win = () {
if (bodyArr.length == 255) {
clearInterval(timer)
alert('游戲勝利!')
location.reload()
}
}
寫(xiě)在最后
游戲到這里就寫(xiě)完了,代碼雖然看起來(lái)多,但是并沒(méi)有什么難的地方,想到就能寫(xiě)。唯一麻煩一點(diǎn)的就是那個(gè)身體的移動(dòng),相通了就很簡(jiǎn)單了。
網(wǎng)頁(yè)題目:如何用原生 JS,快速寫(xiě)一個(gè)貪吃蛇小游戲
文章出自:http://fisionsoft.com.cn/article/cceogsg.html


咨詢
建站咨詢
