做一个类似Cut The Rote 的游戏(五)
在上一篇文章中,鳄鱼吃掉菠萝、菠萝掉到水里或者飞出屏幕,游戏就结束了。这不是我们想要的。今天要修改一下游戏的逻辑。
重置游戏
如果菠萝落在鳄鱼的嘴里,鳄鱼就会快乐地嚼起菠萝。但一旦发生这种情况,游戏就会停止。
在GameScene.swift中,找到switchToNewGameWithTransition()
并添加以下代码:
let delay = SKAction.wait(forDuration: 1)
let sceneChange = SKAction.run({
let scene = GameScene(size: self.size)
self.view?.presentScene(scene, transition: transition)
})
run(SKAction.sequence([delay, sceneChange]))
上述代码,调用SKView
的presentScene(_:transition:)
方法来呈现下一场景。
在这种情况下,下一个场景也是GameScene
实例。您可以使用SKTransition来作为参数设定转换的过渡效果。可以根据不同的游戏结果设置不同的transition,展示不一样的转场效果。
回到didBegin()
方法,在if
条件里面,在奖品收缩和咀嚼动画之后,添加以下内容:
//转换到下一场景
switchToNewGameWithTransition(SKTransition .doorway(withDuration:1.0))
使用SKTransition.doorway(withDuration:)
初始化transition,然后作为switchToNewGameWithTransition()
的参数,并调用该方法,创建转换效果。展示开门的效果,并且进入游戏的下一局。很整洁啊!
结束比赛
您可能会觉得,给我们的水背景也添加physics body,这样就可以检测到菠萝是否掉落水中。但是如果菠萝从屏幕的侧面飞出,那么就检测不到了。一个更简单、更好的方法就是:检测菠萝是否已经移出屏幕底边,如果是,则结束游戏。
SKScene
提供了update()
,该方法每帧调用一次。找到该方法,并添加以下逻辑:
if prize.position.y <= 0 {
switchToNewGameWithTransition(SKTransition.fade(withDuration: 1.0))
}
该if
语句检查奖品的y坐标是否小于零(屏幕的底部)。如果是,它会调用switchToNewGameWithTransition()
方法,以SKTransition.fade(withDuration:)
淡出的方式再次启动游戏。
编译并运行项目。
玩家获胜或失败时,您应该看到场景过渡到新场景。
添加声音和音乐
我们选择了一首来自incompetech.com的的丛林之歌,以及来自freesound.org的的一些音效。
SpriteKit会为您处理音效。但是你会用AVAudioPlayer
在场景转换之间无缝播放背景音乐。
向GameScene.swift添加另一个属性:
private static var backgroundMusicPlayer:AVAudioPlayer!`
声明了一个静态属性,所以所有的GameScene
实例都可以访问到同一个backgroundMusicPlayer
实例。找到该setUpAudio()
方法并添加以下代码:
if GameScene.backgroundMusicPlayer == nil {
let backgroundMusicURL = Bundle.main.url(forResource: SoundFile.BackgroundMusic, withExtension: nil)
do {
let theme = try AVAudioPlayer(contentsOf: backgroundMusicURL!)
GameScene.backgroundMusicPlayer = theme
} catch {
// couldn't load file :[
}
GameScene.backgroundMusicPlayer.numberOfLoops = -1
}
上面的代码检查backgroundMusicPlayer
是否已经创建。如果没有,它将使用你之前添加到Constants.swift的常量来初始化AVAudioPlayer
(该常量已经转化为 URL
),并将其分配给backgroundMusicPlayer。numberOfLoops
值设置为-1,表示该歌曲将无限循环播放。
接下来,将下面的代码添加到setUpAudio()
方法的底部:
if !GameScene.backgroundMusicPlayer.isPlaying {
GameScene.backgroundMusicPlayer.play()
}
当场景首次加载时,启动背景音乐播放(无限循环播放,直到应用程序退出或在其他方法调用播放器stop()
方法)。你也可以不用检查播放器是否正在播放,而直接调用play()
方法,但是这样的话,如果音乐在游戏开始前就已经播放,调用play()
方法是不会跳过当前的音乐或重新播放。
此时,您最好先将要使用的所有音效都设置好。音效不像是音乐需要立即播放的。相反,您更希望创建一些可重用的SKAction
,可以用于稍后播放音效。
返回到GameScene
类定义的顶部,并添加以下属性:
private var sliceSoundAction: SKAction!
private var splashSoundAction: SKAction!
private var nomNomSoundAction: SKAction!
现在回到setUpAudio()
并添加以下行到方法的底部:
sliceSoundAction = SKAction.playSoundFileNamed(SoundFile.Slice, waitForCompletion: false)
splashSoundAction = SKAction.playSoundFileNamed(SoundFile.Splash, waitForCompletion: false)
nomNomSoundAction = SKAction.playSoundFileNamed(SoundFile.NomNom, waitForCompletion: false)
此代码使用SKAction
的playSoundFileNamed(_:waitForCompletion:)
方法初始化播放音效的action。现在是时候播放音效了。
向上滚动到方法update()
,并在if
在调用switchToNewGameWithTransition()
语句之前添加以下代码行:
run(splashSoundAction))`
当菠萝落在水中时,会发出飞溅的声音。接下来,找到didBegin()
并在runNomNomAnimationWithDelay(0.15)
语句下面添加一行代码:
run(nomNomSoundAction)
当鳄鱼抓住她的奖品时,会发出大嚼的声音。最后,找到checkIfVineCutWithBody()
方法,并在if
语句中添加以下代码行:
run(sliceSoundAction)
当玩家剪断葡萄藤的时候,会发出一个扫动的声音。
编译并运行项目。
你发现了一个bug吗?如果鳄鱼没有接到菠萝,会播放多次飞溅的声音。这是由于在游戏过渡到下一个场景之前,重复地触发“游戏结束”的逻辑。要纠正这一点,请在类的顶部添加一个新的state属性:
private var levelOver = false`
在方法update()
和didBegin()
的顶部,都添加以下代码:
if levelOver {
return
}
最后,同一方法的另外一个if
中,将levelOver状态设置为true:
levelOver = true
现在,一旦游戏检测到levelOver
标志已经被设置(菠萝撞到地面,或者鳄鱼吃了菠萝),它将停止检查游戏的胜利/失败,并且不会再持续地播放这些音效。编译并运行。没有尴尬的声效了!
添加触觉反馈
iPhone 7配备了一个新的taptic引擎,为用户提供触摸反馈。它最有名的是模拟点击主页按钮(没有移动部分)。多谢UIFeedbackGenerator
类,开发人员也可以通过几行代码就实现触碰按钮。
您将使用UIImpactFeedbackGenerator
的子类为精灵碰撞添加一些小小的改变。这个类带有三个设置:light
,medium
和heavy
。当鳄鱼咀嚼菠萝时,添加heavy
影响。当菠萝飞出屏幕之外时,添加light
影响。
首先,实例化反馈生成器。在GameScene.swift中,在方法didMove()
之前添加以下属性:
let chomp = UIImpactFeedbackGenerator(style: .heavy)
let splash = UIImpactFeedbackGenerator(style: .light)`
接下来,使用impactOccurred()
方法触发反馈。
找到update()
方法,在run(splashSoundAction)
下方,添加以下内容:
splash.impactOccurred()
接下来,找到didBegin()
并在run(nomNomSoundAction)
下面添加以下内容:
chomp.impactOccurred()
在iPhone 7上编译和运行游戏,感受触觉反馈。
如果您想了解更多关于触觉反馈的信息,请查看这个简短的视频教程,iOS 10:提供触觉反馈。
增加一些难度
玩了几轮后,游戏似乎有点轻松。你会很快找到一个合适的位置,切断三根葡萄藤,然后喂养鳄鱼。
让我们通过使用您之前设置的常量CanCutMultipleVinesAtOnce
来让游戏的难度增大。
在GameScene.swift中,在GameScene
类定义的顶部添加最后一个属性:
private var vineCut = false
找到checkIfVineCutWithBody()
方法,并在方法的顶部添加下面的if
语句:
if vineCut && !GameConfiguration.CanCutMultipleVinesAtOnce {
return
}
将下面这行代码添加到同一方法的底部:
vineCut = true
找到touchesMoved()
方法,并在该方法上面添加下面的方法:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
vineCut = false
}
这样,当用户触摸屏幕时,您将重置vineCut
。
编译构建并运行游戏。你现在应该看到,每次滑动时,只能剪断一根葡萄藤。要剪切另一根,你必须抬起手指,然后再次滑动。
从哪里来?
在这里下载完成的示例项目。
但不要停止!尝试添加新关卡、不同的葡萄藤,还可以显示分数和游戏用时。
如果您想了解更多关于SpriteKit的信息,请务必查看我们的书籍,“ 2D Apple Games”。
如果您有任何问题或意见,请随时加入以下讨论!
球队
www.raywenderlich.com 上的每个教程都是由专门的开发人员团队创建的,具有高质量标准。本教程的团队成员有:
-
Kyle Gorlick 技术编辑
-
克里斯·贝兰格 编辑
-
克里斯语言 校对
-
Tammy Coron 团队领导