之前把贪吃蛇的蛇头移动已经处理完毕,接下来咱们要讨论的就是,如何处理蛇身的跟随。
首先知道的是,蛇身移动的方向和位置都是蛇头走过的轨迹,所以我们要把蛇头的轨迹记录下来
// 记录蛇头走过的点 this.headTrackArray 为定义的属性
this.headTrackArray.push({ postion: this.head.getPosition(), angle: this.head.angle })
将蛇头行动轨迹记录好后,接下来就是让每个蛇身依次按照这个轨迹走,为蛇身新增一个curIndex属性,用于记录该蛇身移动到蛇头轨迹的哪一步了。另外蛇身不可以事实跟随蛇头的轨迹,否则会出现蛇身和蛇头重合的情况,所以还需要为蛇身设置一个移动延迟,我们将称为步长。以下为新增蛇身的方法
/**
* 添加蛇身
*/
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)
}
添加蛇身的方法已经完成了,接下来就是让所有蛇身依次按照蛇头轨迹移动就可以了
for (let i = 0; i < this.tailNodeArray.length; i++) {
var tci = this.tailNodeArray[i].getComponent(Tail).curIndex
if (tci >= 0) {
// 最后一个尾巴节点进行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
}
以上代码,已经可以让所有蛇身正确的跟随蛇头或前一个蛇身移动了,但存在一个问题,就是蛇头的移动轨迹无限push,其实蛇头的移动轨迹列表轨迹元素在最后一个蛇身行动后,已经是无意义的冗余数据,也不会有用到的地方,那么蛇头移动轨迹列表只增不减,肯定不科学。当前正在思考解决该问题,更新中
3 hours later...
可能是自身算法能力不过关,一直没有想到好的思路,有机会一定要恶补一下算法的知识。无意中在网上看其他贪吃蛇项目的文章,看到一句话让我灵光乍现,蛇移动和移动轨迹的逻辑,归根结底就是:蛇头做插入,蛇尾做删除。于是重新修改代码,将添加蛇身的方法修改如下
addNewTail() {
// 取消delay,因为每次蛇做移动蛇尾都会做一次删除,则改为当前蛇身长度对应蛇头轨迹,这个是固定值,根据当前蛇身长度固定。
this.tailNodeArray.push(newTail)
newTail.getComponent(Tail).trackIndex = this.sectionLen * this.tailNodeArray.length
}
然后修改蛇身移动的循环逻辑:
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){
// 最后一个尾巴节点进行shift操作,蛇头做插入,蛇尾做删除
var hta = this.headTrackArray.shift()
}
this.tailNodeArray[i].setPosition(hta.postion)
this.tailNodeArray[i].angle = hta.angle
this.tailNodeArray[i].setScale(this.scoreScale)
}
}
经过以上改动,蛇头的移动轨迹list就不会只增不减,长度始终保持为当前蛇身总长*步长。然后运行,出现另外一个问题,每次蛇身增加一个节点是会顿一下。原因是因为蛇身加长了,但蛇头移动轨迹需要继续行走一个步长来指引新蛇尾的移动。继续修改代码。
3 hours later...
通过反复试验,发现以上说法并不正确,蛇身增加会顿一下的原因其实为当蛇身增加时,最后一个节点增加,然后每个身体节点的trackIndex是固定值,当我们为headTrackArray进行push时,由于蛇身变成,开始的步长内并未对进行headTrackArray进行删除,导致身体刚增加的步长内,身体其实是在原地移动,这才出现了增长蛇身时会停顿一个步长的情况。
以上问题,我们取消原来push的插入方式,改为unshift
(向数组的开头添加一个或更多元素,并返回新的长度。),这样就能保持蛇身trackIndex指向的轨迹为正确的。因为是通过unshift插入,所以我们最后一节读取轨迹时,这边使用pop(删除数组的最后一个元素并返回删除的元素)来获取并删除最后一个元素
// 记录蛇头走过的点
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) {
// 最后一个尾巴节点进行shift操作,蛇头做插入,蛇尾做删除
var hta = this.headTrackArray.pop()
}
this.tailNodeArray[i].setPosition(hta.postion)
this.tailNodeArray[i].angle = hta.angle
this.tailNodeArray[i].setScale(this.scoreScale)
}
}
已经完成,不过比较不完美的是,unshift插入的效率并不高,远不如push。