Chapter 10. Adding sound effects

We now have a fully functional game with scores. It is time to add some sound effects. This is fairly easy to do using Sprite Kit in Swift. Every game has to have background music, so we will see how to add audio and what design patterns we can use to control the audio. We will not only add background music, but we will also look at adding some sound effects for our character when he has acquired a power up or when he dies.

In this chapter we will learn how to add sound to our game using:

  1. AVAudioPlayer
  2. SKAction

AVAudioPlayer is part of the AVFoundation framework, which is used when dealing with audio and video in iOS or Mac OS X. It makes it easy to play audio and provides you with fine tune controlling.

SKAction is part of the Sprite Kit framework that makes playing sound super easy, and we will cover that as well in this chapter.

Adding background music

Before starting we need to select a background music appropriate for the game. A good resource to find free royalty free audio is OpenGameArt.org. The purpose of the site is to help free/open source game developers get access to license/royalty free artwork and sound. Another way you can create original music or sound effects for your game is through Garageband App, available for free by Apple on their App Store. And if you don’t want to do it yourself, you can get someone to do it for you on Fiverr.

iOS supports many industry standard audio formats including the popular mp3, Microsoft’s wav and Apple’s aiff format. The best choice is to use a compressed audio format such as mp3 to reduce the size of the application, making it quicker to download. Once you have the audio file in one of those formats, you can start by adding it to your Xcode project by dragging it into your Resources group.

All files in the application are bundled during the time of the build. You can access any resource from the main bundle and then by requesting the path to the file that you want. The file can then be read by providing this path. To get the audio resource path from the Main bundle:

NSBundle.mainBundle().pathForResource("background", ofType: "mp3")

In our game we have named the background music file “background,” and it is encoded in the mp3 format. You can pass the extension in the file name and pass .None in the type as well, but the former is better. With the path to the file you can either read it from the bundle as NSData type and pass it to the AVAudioPlayer, or you can use one of the convenient initializer methods that AVAudioPlayer provides that takes a URL to the audio file. A local path to a file is considered a valid URL and we can use NSURL to create a URL from the file path.

You can read the full source code of our Pencil Adventure game here on Github.

For playing the music we will use AVAudioPlayer as mentioned before. There are different ways to implement audio in a game, but we chose to use AVAudioPlayer for its ability to play, pause, stop and seek an audio track. You can also use:

SKAction.playSoundFileNamed

SKAction is limited in functionality and is best suited for short sounds such as the Hero making a sound on jumping or getting a powerup. We will show how to use SKAction to play audio in the next section of this chapter.

Once you have the URL object created using NSURL.fileURLWithPath, you can create a player instance by calling its initializer and passing the URL. You can also pass a pointer to the error variable in the initializer, and that variable will be populated with the error if it is not able to play the audio file for some reason. This pattern of passing error variable and then checking later if it is populated is there for backward compatibility with the Objective-C code, since currently new Swift APIs have not been created for these classes.

AVAudioPlayer(contentsOfURL: url, error: &err)

Before we continue let’s examine the overall design for implementing sound in our game. Instead of creating multiple AVAudioPlayer instances, it would be best to have one instance that plays the background sound, since our game will only play one audio file at a given time. Also, having just one instance will help us avoid having multiple audio files playing at the same time, and save the CPU and memory resources of the device.

With this is mind, the best design pattern to use for playing the background music is to use a Singleton pattern, where we have one Sound Manager class that handle the playback of the audio file. Sound Manager will act as a wrapper to access the AVAudioPlayer instance. We will use a singleton design pattern for our Sound Manager.

Singleton is a design pattern where only one instance is created throughout the execution of the program. Singleton objects are a good place to store global variables like a config object. Singleton objects can also be used when only one instance of an object is needed, such as a Queue manager for a printer.

To create a singleton instance we are calling an Objective-C wrapper function, dispatch_once, which takes a token to figure out whether the block of code has been evaluated so that it does not run it again. For the game code in Sound Manager we pass a pointer to the token, which is part of the legacy API in Objective-C dispatch_once(&onceToken).

We have cheated a little and added class or static methods to the SoundManager class to make it convenient to call play, pause, stop and restart it without having to first get a shared instance. Ideally, in a singleton pattern, you would have a method that would return a shared instance and use dispatch_once to create create it only once. Here in our SoundManager we have declared our shared instance under the variable name SharedPlayer.

One of the best features of Swift is computed variables. We are using BackgroundPlayer as a computed variable where we create a shared instance called SharedPlayer once and return that in the getter closure. We have purposefully not added a setter, since it does not make sense here, given that we are making the BackgroundPlayer a read only variable.

In our dispatch_once closure block we first initialize the AVAudioPlayer and if there is no err value set in our err pointer, we can prepare the player to play and set out the number of loops to -1 to have the background music repeat forever.

dispatch_once(&onceToken) {
    let url = NSURL.fileURLWithPath(NSBundle.mainBundle().pathForResource("background", ofType: "mp3"))
    var err: NSError?
    SharedPlayer = AVAudioPlayer(contentsOfURL: url, error: &err)
    if let error = err {
        NSLog("Error %@", error)
    } else {
        SharedPlayer.prepareToPlay()
        SharedPlayer.numberOfLoops = -1
    }
}

The computed property in Swift comes in real handy and acts more like a before filter like in Rails framework. Instead of referencing SharedInstance directly we refer to BackgroundPlayer throughout the code in SoundManager class as it will create the SharedPlayer if it is not created and pass the reference to it in future calls.

In Swift you are not allowed to have expressions at a top level in a Swift file, but variables can be declared. Also these variables can be accessed in other Swift files in the same project so to hide them and to not pollute the global variable namespace we have declared these as private to be accessed only within the file by SoundManager.

Adding sound effects

Another way to add sound to the game is through SKAction. This is the quickest and easiest way, but it is not as flexible as using AVAudioPlayer. It’s as simple as creating an SKAction object and telling the scene to run that action.

myScene.runAction(SKAction.playSoundFileNamed("SomeSound.mp3", waitForCompletion: false))

Looking at this example you can see that it just takes the audio file name and plays it right away. This is great for adding short effects to your character or in general throughout the game. The playback is hard to control so it will not be suitable for long tracks of audio, but it can be used for that. The only caveat to this is that because it is an action on the scene you will need to tell the scene to stop playing the sound file by removing that action. SKAction.playSoundFileNamed would be best used for adding a sound effect when the characters in your game get a power up, fall or get hit in the game. It does not require a lot of ceremony to setup like in Sound Manager, so use it wisely throughout the game.

Summary

AVAudioPlayer and SKAction are two great ways to play audio in your game. One has more power and control over ease of use. It is important to understand these trade offs and use what makes sense in your use case. In our game we used both for different purposes. In the next chapter we will cover the V in the AV, which is how to add visual effects in the game.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset