本文任务
- 游戏运行中,Foreground地面持续滚动。
持续移动地面
任务一需要解决的问题:
- 如何移动地面。
- 如何无缝连接。
问题一的解决思路是每次渲染完毕进入update()
方法中更新Foreground的坐标位置,即改变position的x值。
问题二的解决思路是实例化两个Foreground,相邻紧挨,以约定好的速度向左移动,当第一个节点位置超出屏幕范围(对玩家来说是不可见)时,改变其坐标位置,添加到第二个节点尾部,如此循环实现无缝连接,参考图为:
代码实现:
找到GameScene类中的setupForeground()
方法,由于现在需要实例化两个Foreground,显然早前的已不再使用,替换方法中的所有内容:
func setupForeground() {
for i in 0..<2{
let foreground = SKSpriteNode(imageNamed: "Ground")
foreground.anchorPoint = CGPoint(x: 0,y: 1)
// 改动1
foreground.position = CGPoint(x: CGFloat(i) * size.width,y: playableStart)
foreground.zPosition = Layer.Foreground.rawValue
// 改动2
foreground.name = "foreground"
worldNode.addChild(foreground)
}
}
注意我们采用for-in
进行2次实例化,代码有两处改动:1.放置位置与i值相关;2.给节点取名为“foreground”,方便之后查找操作。
Foreground匀速移动,自然速度值需要固定,姑且这里设为150.0,请在let kImpulse: CGFloat = 400.0
下方添加一行速度常量定义let kGroundSpeed: CGFloat = 150.0
。
对于Foreground的位置更新自然也是在方法update()
中进行了,每隔大约33毫秒就跳入该函数更新position。就像早前updatePlayer()
一样,在其下方声明一个名为updateForeground方法。
func updateForeground(){
//1
worldNode.enumerateChildNodesWithName("foreground") { (node,stop) -> Void in
//2
if let foreground = node as? SKSpriteNode{
//3
let moveAmt = CGPointMake(-self.kGroundSpeed * CGFloat(self.dt),0)
foreground.position += moveAmt
//4
if foreground.position.x < -foreground.size.width{
foreground.position += CGPoint(x: foreground.size.width * CGFloat(2),y: 0)
}
}
}
}
讲解:
- 还记得先前设置了Foreground节点的名字为foreground吗?通过
enumerateChildNodesWithName
方法即可遍历所有名为foreground的节点。 - 注意node是
SKNode
类型,而foreground精灵是SKSpriteNode
类型,需要向下变形。 - 计算dt时间中foreground移动的距离,更新position坐标位置。
- 倘若该foreground超出了屏幕,则正如前面所说的将其添加到第二个精灵尾部。
4中的位置条件判断,希望读者理解透彻。首先SpriteKit中坐标系与之前不同,原点位于左下角,x轴正方向自左向右,y轴正方向自下向上;其次wordNode节点位于原点处,因此它内部的坐标系也是以左下角为原点。请集合上文图片进行理解。