之前把貪吃蛇的蛇頭移動已經(jīng)處理完畢,接下來咱們要討論的就是,如何處理蛇身的跟隨。
首先知道的是,蛇身移動的方向和位置都是蛇頭走過的軌跡,所以我們要把蛇頭的軌跡記錄下來
// 記錄蛇頭走過的點(diǎn) this.headTrackArray 為定義的屬性
this.headTrackArray.push({ postion: this.head.getPosition(), angle: this.head.angle })
將蛇頭行動軌跡記錄好后,接下來就是讓每個蛇身依次按照這個軌跡走,為蛇身新增一個curIndex屬性,用于記錄該蛇身移動到蛇頭軌跡的哪一步了。另外蛇身不可以事實(shí)跟隨蛇頭的軌跡,否則會出現(xiàn)蛇身和蛇頭重合的情況,所以還需要為蛇身設(shè)置一個移動延遲,我們將稱為步長。以下為新增蛇身的方法
/**
* 添加蛇身
*/
addNewTail() {
var pos = new Vec3()
var delay = this.sectionLen * (this.tailNodeArray.length + 1)
if (this.tailNodeArray.length > 1) {
// 跟隨最后一個尾巴的位置
var lastTail = this.tailNodeArray[this.tailNodeArray.length - 1]
pos = lastTail.getPosition()
// 移動延遲為:基于最后一個尾巴的位置 - 步長
delay = lastTail.getComponent(Tail).curIndex + this.sectionLen
}
let newTail = instantiate(this.tail)
this.node.insertChild(newTail, 0)
newTail.setPosition(pos)
newTail.getChildByName('Label').getComponent(Label).color = this.snakeColor
newTail.getComponent(Tail).curIndex = delay
this.tailNodeArray.push(newTail)
}
添加蛇身的方法已經(jīng)完成了,接下來就是讓所有蛇身依次按照蛇頭軌跡移動就可以了
for (let i = 0; i < this.tailNodeArray.length; i++) {
var tci = this.tailNodeArray[i].getComponent(Tail).curIndex
if (tci >= 0) {
// 最后一個尾巴節(jié)點(diǎn)進(jìn)行shift操作
// var hta = this.headTrackArray.shift()
var hta = this.headTrackArray[tci]
this.tailNodeArray[i].setPosition(hta.postion)
this.tailNodeArray[i].angle = hta.angle
this.tailNodeArray[i].setScale(this.scoreScale)
}
this.tailNodeArray[i].getComponent(Tail).curIndex += 1
}
以上代碼,已經(jīng)可以讓所有蛇身正確的跟隨蛇頭或前一個蛇身移動了,但存在一個問題,就是蛇頭的移動軌跡無限push,其實(shí)蛇頭的移動軌跡列表軌跡元素在最后一個蛇身行動后,已經(jīng)是無意義的冗余數(shù)據(jù),也不會有用到的地方,那么蛇頭移動軌跡列表只增不減,肯定不科學(xué)。當(dāng)前正在思考解決該問題,更新中
3 hours later...
可能是自身算法能力不過關(guān),一直沒有想到好的思路,有機(jī)會一定要惡補(bǔ)一下算法的知識。無意中在網(wǎng)上看其他貪吃蛇項(xiàng)目的文章,看到一句話讓我靈光乍現(xiàn),蛇移動和移動軌跡的邏輯,歸根結(jié)底就是:蛇頭做插入,蛇尾做刪除。于是重新修改代碼,將添加蛇身的方法修改如下
addNewTail() {
// 取消delay,因?yàn)槊看紊咦鲆苿由呶捕紩鲆淮蝿h除,則改為當(dāng)前蛇身長度對應(yīng)蛇頭軌跡,這個是固定值,根據(jù)當(dāng)前蛇身長度固定。
this.tailNodeArray.push(newTail)
newTail.getComponent(Tail).trackIndex = this.sectionLen * this.tailNodeArray.length
}
然后修改蛇身移動的循環(huán)邏輯:
for (let i = 0; i < this.tailNodeArray.length; i++) {
var tci = this.tailNodeArray[i].getComponent(Tail).curIndex
if (this.headTrackArray.length > tci) {
var hta = this.headTrackArray[tci]
if (i == this.tailNodeArray.length-1){
// 最后一個尾巴節(jié)點(diǎn)進(jìn)行shift操作,蛇頭做插入,蛇尾做刪除
var hta = this.headTrackArray.shift()
}
this.tailNodeArray[i].setPosition(hta.postion)
this.tailNodeArray[i].angle = hta.angle
this.tailNodeArray[i].setScale(this.scoreScale)
}
}
經(jīng)過以上改動,蛇頭的移動軌跡list就不會只增不減,長度始終保持為當(dāng)前蛇身總長*步長。然后運(yùn)行,出現(xiàn)另外一個問題,每次蛇身增加一個節(jié)點(diǎn)是會頓一下。原因是因?yàn)樯呱砑娱L了,但蛇頭移動軌跡需要繼續(xù)行走一個步長來指引新蛇尾的移動。繼續(xù)修改代碼。
3 hours later...
通過反復(fù)試驗(yàn),發(fā)現(xiàn)以上說法并不正確,蛇身增加會頓一下的原因其實(shí)為當(dāng)蛇身增加時,最后一個節(jié)點(diǎn)增加,然后每個身體節(jié)點(diǎn)的trackIndex是固定值,當(dāng)我們?yōu)閔eadTrackArray進(jìn)行push時,由于蛇身變成,開始的步長內(nèi)并未對進(jìn)行headTrackArray進(jìn)行刪除,導(dǎo)致身體剛增加的步長內(nèi),身體其實(shí)是在原地移動,這才出現(xiàn)了增長蛇身時會停頓一個步長的情況。
以上問題,我們?nèi)∠瓉韕ush的插入方式,改為unshift
(向數(shù)組的開頭添加一個或更多元素,并返回新的長度。),這樣就能保持蛇身trackIndex指向的軌跡為正確的。因?yàn)槭峭ㄟ^unshift插入,所以我們最后一節(jié)讀取軌跡時,這邊使用pop(刪除數(shù)組的最后一個元素并返回刪除的元素)來獲取并刪除最后一個元素
// 記錄蛇頭走過的點(diǎn)
this.headTrackArray.unshift({ postion: this.head.getPosition(), angle: this.head.angle })
for (let i = 0; i < this.tailNodeArray.length; i++) {
var ti = this.tailNodeArray[i].getComponent(Tail).trackIndex
if (this.headTrackArray.length > ti) {
var hta = this.headTrackArray[ti]
if (i == this.tailNodeArray.length - 1) {
// 最后一個尾巴節(jié)點(diǎn)進(jìn)行shift操作,蛇頭做插入,蛇尾做刪除
var hta = this.headTrackArray.pop()
}
this.tailNodeArray[i].setPosition(hta.postion)
this.tailNodeArray[i].angle = hta.angle
this.tailNodeArray[i].setScale(this.scoreScale)
}
}
已經(jīng)完成,不過比較不完美的是,unshift插入的效率并不高,遠(yuǎn)不如push。