Making a simple spritekit game. There I have sounds like button touched sound and etc. did by AVFoundation
sound library. But have trouble with this. My frame rate always drops when hit any button, which sounds. But if I mute all my sound, there is no lag. Here is my code:
import AVFoundation
class GameAudio {
static let shared = GameAudio()
private var buttonClickSound = AVAudioPlayer()
private var completionSound = AVAudioPlayer()
private var swishSound = AVAudioPlayer()
private var gridSwishSound = AVAudioPlayer()
private let Volume: Float = 0.8
func setupSounds() {
print("GameAudio setupSounds()")
if let path = Bundle.main.path(forResource: SoundNames.ButtonClickSoundName, ofType: "mp3") {
let url = URL(fileURLWithPath: path )
do {
buttonClickSound = try AVAudioPlayer(contentsOf: url)
} catch {
print(error)
}
}
if let path = Bundle.main.path(forResource: SoundNames.CompletionSoundName, ofType: "mp3") {
let url = URL(fileURLWithPath: path)
do {
completionSound = try AVAudioPlayer(contentsOf: url)
} catch {
print(error)
}
}
if let path = Bundle.main.path(forResource: SoundNames.SwishSoundName, ofType: "mp3") {
let url = URL(fileURLWithPath: path )
do {
swishSound = try AVAudioPlayer(contentsOf: url)
} catch {
print(error)
}
}
if let path = Bundle.main.path(forResource: SoundNames.GridSwishSoundName, ofType: "mp3") {
let url = URL(fileURLWithPath: path)
do {
gridSwishSound = try AVAudioPlayer(contentsOf: url)
} catch {
print(error)
}
}
buttonClickSound.volume = Volume
completionSound.volume = Volume
swishSound.volume = Volume
gridSwishSound.volume = Volume
}
func playButtonClickSound() {
buttonClickSound.play()
}
func playCompletionSound() {
completionSound.play()
}
func playSwishSound() {
swishSound.play()
}
func playGridSwishSound() {
gridSwishSound.play()
}
}
In my GameViewController I call GameAudio.shared.setupSounds()
for preload all my sounds.
public struct Actions {
static func buttonIsTouched(_ button: SKNode, sound: Bool, completion: @escaping () -> ()) {
if button.hasActions() { return }
if sound {
GameAudio.shared.playButtonClickSound()
}
button.run(touchedButtonAction(scaleFactor: button.returnScaleFactor()), completion: completion)
}
}
class MenuNode: SKNode {
private let settings: Settings
var delegate: MenuNodeDelegate?
private let playButton = SKSpriteNode(imageNamed: SpriteNames.PlayButtonName)
private let MenuNodeTreshold: CGFloat = 12.0
init(settings: Settings) {
self.settings = settings
super.init()
setupButtons()
isUserInteractionEnabled = false
}
private func setupButtons() {
playButton.position = CGPoint(x: 0.0 - playButton.frame.size.width / 2.0 - MenuNodeTreshold / 2.0, y: 0.0)
addChild(playButton)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if playButton.contains(location) {
Actions.buttonIsTouched(playButton as SKNode, sound: settings.sound, completion: {
self.delegate?.playButtonTouched()
})
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I use Actions
struct for all my actions in my game. There I have buttonTouchedAction()
, I use it in different nodes. I put some code from my menu with one "play" button. So, for example, I run my game on device, it shows up menu node, wait just few seconds and tap the button, after what my frame rate drops, I have a lag in a second and then scene changes to game scene.
If you want to use AVAudioPlayer
(instead of SpriteKit's
sound actions), you should make the sounds play in a background thread so that its operation won't interfere with the main thread where all of your visual stuff goes:
let soundQueue = OperationQueue()
soundQueue.qualityOfService = QualityOfService.background
soundQueue.addOperation{self.buttonClickSound.play()}