
2.5 滚动场景
在很多的游戏中,场景都不是静止的,而是滚动的,如在植物大战僵尸的游戏中,它的场景如图2.26所示。

图2.26 植物大战僵尸
在图2.26中,用黑色框框住的部分是在屏幕上显示的,即玩家是可以看到的。右边的这些内容玩家就看不到了,为了让玩家可以熟悉场景中的内容,此游戏在开始时,首先对屏幕中的场景进行滚动。本节将为开发者实现场景滚动的功能。
2.5.1 让场景进行滚动
以下是如何让一个场景进行滚动的具体实现方法。
1.创建项目以及添加图像
从上文的内容中可以看到场景的背景颜色具有单一性,即场景的背景颜色是红色,场景就为红色。一般所说的滚动场景其实就是滚动背景,所以在滚动场景时,场景的滚动效果是看不出来的。为了可以看到滚动效果,背景需要使用图像实现。首先我们需要添加图像background1.png和background2.png到创建的项目中。添加图像的具体步骤如下:
(1)创建一个Game类型的项目,命名为2-2。
(2)右击2-2项目中的Supporting Files文件夹,弹出快捷菜单,选择Add Files to "2-2"…命令,如图2.27所示。

图2.27 快捷菜单
(3)弹出选择文件对话框,如图2.28所示。

图2.28 选择文件对话框
(4)选择需要添加的图像后,单击Add按钮,实现图像的添加。添加后的图像就会显示在Supporting Files文件夹中。
2.修改设备的方向
单击项目名称,打开目标窗口,选择General选项,打开General面板,在其中找到Device Orientation,选中其中的Landscape Left和Landscape Right复选框,如图2.29所示,让运行的模拟器方向变为横向。

图2.29 修改设备方向
3.删除多余的代码
打开GameScene.swift文件,删除多余代码,剩余的代码如下:
import SpriteKit class GameScene: SKScene { override func didMoveToView(view: SKView) { } override func update(currentTime: CFTimeInterval) { } }
打开GameViewController.swift文件,删除多余代码,剩余的代码如下:
import UIKit import SpriteKit class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } }
4.编写实现滚动场景的代码
(1)打开GameScene.swift文件,添加一个新的框架,以及实现运算符的重载,使其可以实现两个点的加法赋值运算以及乘法运算。代码如下:
import CoreGraphics //“+”加法运算符的重载 func + (left: CGPoint, right: CGPoint) -> CGPoint { return CGPoint(x: left.x + right.x, y: left.y + right.y) } //“+=”赋值加法运算符的重载 func += (inout left: CGPoint, right: CGPoint) { left = left + right } //“*”乘法运算符的重载 func * (point: CGPoint, scalar: CGFloat) -> CGPoint { return CGPoint(x: point.x * scalar, y: point.y * scalar) }
(2)添加一个backgroundNode()方法,此方法实现的功能是将两个图像作为场景中的背景,如图2.30所示。代码如下:
func backgroundNode() -> SKSpriteNode { //创建并设置backgroundNode对象 let backgroundNode = SKSpriteNode() backgroundNode.anchorPoint = CGPointZero backgroundNode.name = "background" //创建并设置backgroundNode1对象 let background1 = SKSpriteNode(imageNamed: "background1") background1.anchorPoint = CGPointZero background1.position = CGPoint(x: 0, y: 0) backgroundNode.addChild(background1) //创建并设置backgroundNode2对象 let background2 = SKSpriteNode(imageNamed: "background2") background2.anchorPoint = CGPointZero background2.position = CGPoint(x: background1.size.width, y: 0) backgroundNode.addChild(background2) //设置backgroundNode对象的尺寸 backgroundNode.size = CGSize( width: background1.size.width + background2.size.width, height: background1.size.height) return backgroundNode }

图2.30 场景的背景
注意:在此代码中的背景添加使用的是SKSpriteNode类实现的,对于此类,我们会在后面进行讲解。
(3)在didMoveToView(view: SKView)方法中编写代码,将设置的背景添加到场景中,代码如下:
override func didMoveToView(view: SKView) {
let background = backgroundNode() //实例化对象
background.anchorPoint = CGPointZero //设置锚点
background.position = CGPointZero //设置位置
background.name = "background"
addChild(background)
}
(4)添加变量到GameScene.swift文件中,代码如下:
let backgroundMovePointsPerSec: CGFloat = 200.0 //背景在每秒钟移动的点 var dt: NSTimeInterval = 0 //时间间距 var lastUpdateTime: NSTimeInterval = 0 //上一次更新的时间
(5)添加一个moveBackground()的方法,在此方法中编写代码,使其实现背景的移动,代码如下:
func moveBackground() { enumerateChildNodesWithName("background") { node, _ in let background = node as! SKSpriteNode //将node转换为SKSpriteNode let backgroundVelocity = CGPoint(x: -self. backgroundMovePointsPerSec, y: 0) //背景的速度 let amountToMove = backgroundVelocity * CGFloat(self.dt) //偏移量 background.position += amountToMove //背景的位置 } }
(6)在update(currentTime: NSTimeInterval)方法中编写代码,实现数据的更新,进而实现场景的滚动,代码如下:
override func update(currentTime: NSTimeInterval) {
//判断lastUpdateTime是否大于0
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
} else {
dt = 0
}
lastUpdateTime = currentTime
moveBackground() //调用
}
(7)打开GameViewController.swift文件,编写代码,实现场景的显示,代码如下:
override func viewDidLoad() { super.viewDidLoad() let scene = GameScene(size:CGSize(width: 2048, height: 1536)) let skView = self.view as! SKView skView.showsFPS = true skView.showsNodeCount = true skView.ignoresSiblingOrder = true scene.scaleMode = .AspectFill skView.presentScene(scene) }
此时运行程序,会看到如图2.31所示的效果。

图2.31 运行效果
2.5.2 永无休止的滚动场景
在2.5.1小节中,当单击运行按钮后,可以看到场景中的背景图像实现了滚动,进而实现了场景的滚动,但是,将这两个图像都滚动完毕后,场景还在进行滚动,此时背景图像就慢慢地退出了场景,如图2.32所示。本小节将为开发者解决在上一小节中的不足,实现一个永无休止的滚动场景功能,即背景循环滚动。具体的操作步骤如下:

图2.32 滚动场景
(1)打开GameScene.swift文件,添加一个变量,代码如下:
let backgroundLayer = SKNode() //实例化一个节点
(2)修改didMoveToView(view: SKView)方法中的代码,修改后的代码如下:
override func didMoveToView(view: SKView) { //变量0到1的范围 for i in 0...1 { //设置并添加背景 let background = backgroundNode() background.anchorPoint = CGPointZero background.position = CGPoint(x: CGFloat(i)*background.size.width, y: 0) background.name = "background" addChild(background) } }
(3)修改moveBackground()方法中的代码,修改后的代码如下:
func moveBackground() { enumerateChildNodesWithName("background") { node, _ in let background = node as! SKSpriteNode let backgroundVelocity =CGPoint(x: -self.backgroundMovePointsPerSec, y: 0) let amountToMove = backgroundVelocity * CGFloat(self.dt) background.position += amountToMove let backgroundScreenPos = self.backgroundLayer.convertPoint (background.position, toNode: self) //判断x的值是否小于背景的-width的值 if backgroundScreenPos.x <= -background.size.width { //设置背景的位置 background.position = CGPoint( x: background.position.x + background.size.width*2, y: background.position.y) } } }
此时运行程序,会看到如图2.33所示的效果。

图2.33 运行效果