致命的音效
音效是游戏的一个重要元素。SpriteKit对音效也提供了支持。
在SpriteKit中有2种方式来播放音效:
- 使用SKAudioNode:
audioNode = SKAudioNode(fileNamed: filename)
audioNode!.autoplayLooped = false
self.addChild(audioNode!)
audioNode!.runAction(SKAction.play())
2.使用SKAction.playSoundFileNamed:
let soundAction = SKAction.playSoundFileNamed("\(towerType.rawValue)Fire.mp3", waitForCompletion: false)
这是Apple官方推荐的方法,几乎所有的SpriteKit教程也都是用的这2种方式。
如果你像我一样,在游戏中有很多音效需要不断播放的话,你会面临两个问题:
1.在多个音效一起播放的时候,你偶然会遇到一个uncaught exception
*** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: _engine->IsRunning()'
2.反复播放后你会发现内存只增不减。
这两个问题都会造成应用崩溃。第一种是偶然性的崩溃,第二种是累积性的崩溃。
所以我说,这是致命的音效。
观察到这个现象后,这个问题困扰我2个星期的时间。查了各种资料。
有人用延迟了解决第一个问题。
比如:
https://forums.developer.apple.com/thread/27980
但是效果并不完美。
解决方法
经过探索我终于意识到,SpriteKit的音效播放本身存在问题。
只有彻底放弃它才可以使问题得到解决。
经过验证,以下2种方式都可以成功:
方法一:AudioServicesPlaySystemSound
var dict = [String:SystemSoundID]()
func play(fileName:String){
if let soundId = dict[fileName] {
AudioServicesPlaySystemSound(soundId)
} else if let soundURL = NSBundle.mainBundle().URLForResource(fileName, withExtension: "mp3") {
var mySound: SystemSoundID = 0
AudioServicesCreateSystemSoundID(soundURL, &mySound)
dict[fileName] = mySound
AudioServicesPlaySystemSound(mySound)
}
}
方法二:AVAudioPlayer
var dict2 = [String:AVAudioPlayer]()
func play(fileName:String){
if let player = dict2[fileName] {
player.play()
} else{
let path = NSBundle.mainBundle().pathForResource(fileName, ofType:"mp3")!
let url = NSURL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOfURL: url)
//player = sound
dict2[fileName] = sound
sound.playAtTime(0)
} catch {
// couldn't load file :(
}
}
}
注意,在我的游戏中,几个声音文件(.mp3)反复播放,所以我将它们放在Dictionary中,避免每次都要加载。
使用后,崩溃没了,内存的使用也终于正常了。