Turning sounds on and off

Right now, we have sound effects and music playing all the time no matter what. Even though you might be someone who enjoys listening to sounds or music, when you design your games (as it's definitely an element that increases user engagement), you would want to be open to the fact that people just don't like to hear any sound at times. Thus, we must give them the option to turn the sound on and off.

No options or settings? Main menu it is!

Since we don't have a pause screen, options, settings, or anything similar, we're going to add the buttons to turn the sound on and off to the main menu. This means that a lot less code is required to add the buttons, instead of creating an entirely new scene exclusively for them. This arrangement also stays consistent with the clean feel of the game.

Tip

If you want to make a pause screen and add these two buttons to it, then go ahead by all means. The code, however, will be slightly different from what will be described here because if you push a CCScene instead of replace (which essentially allows you to pause the game while going to a new scene temporarily), and then pop off the scene that you pushed (in other words, resume the paused game), you need to make sure that the correct variables get set to false.

Creating the buttons

First, we're going to create the sound and music on/off buttons in the main menu. These are going to be a bit different from normal buttons because instead of just a normal button press, they're going to need to swap between the on and off states.

In MenuScene.m, let's create the initial buttons in the init method:

if ((self=[super init]))
{
    
    //these values range 0 to 1.0, so use float to get ratio
    CCNode *background = [CCNodeColor nodeWithColor:[CCColor whiteColor]];
    [self addChild:background];
    
    winSize = [CCDirector sharedDirector].viewSize;
    CCButton *btnPlay = [CCButton buttonWithTitle:@"" spriteFrame:[CCSpriteFrame frameWithImageNamed: @"btnPlay.png"]];
    btnPlay.position = ccp(winSize.width/2, winSize.height * 0.5);
    [btnPlay setTarget:self selector:@selector(goToGame)];
    [self addChild:btnPlay];
    
    //add the sound and music buttons:
    
    CCButton *btnSound = [CCButton buttonWithTitle:@"" spriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"btnSoundOn.png"]];
    [btnSound setBackgroundSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: @"btnSoundOff.png"] forState:CCControlStateSelected];
    btnSound.position = ccp(winSize.width * 0.35, winSize.height * 0.2);
    [btnSound setTogglesSelectedState:YES];
    [btnSound setTarget:self selector:@selector(soundToggle)];
    [self addChild:btnSound];
    
    CCButton *btnMusic = [CCButton buttonWithTitle:@"" spriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"btnMusicOn.png"]];
    [btnMusic setBackgroundSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"btnMusicOff.png"] forState:CCControlStateSelected];
    btnMusic.position = ccp(winSize.width * 0.65, winSize.height * 0.2);
    [btnMusic setTogglesSelectedState:YES];
    [btnMusic setTarget:self selector:@selector(musicToggle)];
    [self addChild:btnMusic];

  ...
}

Then we need to make sure we create the methods that the buttons will be calling when they're pressed:

-(void)soundToggle
{
  
}

-(void)musicToggle
{
  
}

Running the game at this point will result in what is shown in the following screenshot. If you press either of the buttons, they will switch between each other, and if you exit and come back, the buttons will be reset to their original state.

Creating the buttons

Creating the keys

What we need to do to grab (and store) the sound and music variables is make use of NSUserDefaults, just as we did in the past. To remove any user error when coding, we want to define constants for our dictionary keys.

Since MainScene has been imported into all of our classes, we can safely define the constant there. So, open MainScene.h and add the following code to the top of the file, along with the other constants:

FOUNDATION_EXPORT NSString *const KeySound;
FOUNDATION_EXPORT NSString *const KeyMusic;

Then, in MainScene.m, add this code at the top of the file with the rest of the constants so that they are defined:

NSString *const KeySound = @"keySound";
NSString *const KeyMusic = @"keyMusic";

Now we're able to grab the data that's been stored as well as efficiently save any values if we need to.

Grabbing the sound and music Boolean from NSUserDefaults

We want to store the data in a variable so that we don't have to read and write again and again from NSUserDefaults, and can do so only when we really need to.

Therefore, in MainScene.h, add two Boolean variables for sound on/off and music on/off, like this:

@interface MenuScene :CCNode
{
  CGSize winSize;
  BOOL isSoundOn, isMusicOn;
}

Then, after you've added the buttons to the scene in the init method of MainScene.m, read the sound and music values from NSUserDefaults using the keys you just defined:

isSoundOn = [[NSUserDefaults standardUserDefaults] boolForKey:KeySound];
isMusicOn = [[NSUserDefaults standardUserDefaults] boolForKey:KeyMusic];

Now we want to actually tell the sound and music buttons whether or not they should show their X mark or check mark. To do so, we'll just set the selected value to whatever the opposite of the variable is. That's because if the sound is not on, we want to show the selected version:

btnSound.selected= !isSoundOn;
btnMusic.selected= !isMusicOn;

This works, but there's no way to test it, so let's actually modify the values when the respective buttons are pressed.

Setting and saving the values

In the soundToggle method, we're going to set the isSoundOn variable to the opposite of itself (toggle it on and off). Right after that, we're going to set (and save) its value to the key we defined earlier:

-(void)soundToggle
{
  isSoundOn = !isSoundOn;
  [[NSUserDefaults standardUserDefaults] setBool:isSoundOn forKey:KeySound];
  [[NSUserDefaults standardUserDefaults] synchronize];
}

Then we're going to do the same for the isMusicOn variable in the musicToggle method:

-(void)musicToggle
{
  isMusicOn = !isMusicOn;
  [[NSUserDefaults standardUserDefaults] setBool:isMusicOn forKey:KeyMusic];
  [[NSUserDefaults standardUserDefaults] synchronize];
}

If you run the game now, you'll be able to switch between the true/false variables of the sound and music, and when you either go to another scene and come back, or exit the game and come back, the values will be retained from whatever you last set them to. But it's still not pausing the music or turning off the sound, so let's fix that.

Pausing/resuming background music and sound

If the background music is playing when the music button is pressed, we'll need to pause it, and vice versa for when it's not playing.

To start, let's go to the musicToggle method and add a check for the isMusicOn variable. If it is enabled, we can play the background music. Otherwise, we'll just pause the music until the user turns it on again:

isMusicOn ? [[OALSimpleAudio sharedInstance] playBg] : [[OALSimpleAudio sharedInstance] bgPaused];

After that, we'll also add a check to see whether the isSoundOn is enabled. If it is, we'll play the buttonClick sound effect:

if (isSoundOn)
[[OALSimpleAudio sharedInstance] playEffect:@"buttonClick.mp3"];

We're going to do the same for the toggleSound method as well as the goToGame method, both of which are methods that get called when a button is pressed. Therefore, we are going to play a button click sound effect (only if the sound is enabled):

-(void)goToGame
{
  if (isSoundOn)
    [[OALSimpleAudio sharedInstance] playEffect:@"buttonClick.mp3"];
  [[CCDirector sharedDirector] replaceScene:[MainScene scene]];
}

-(void)soundToggle
{
  ...
  
  if (isSoundOn)
    [[OALSimpleAudio sharedInstance] playEffect:@"buttonClick.mp3"];
}

For MenuScene, all is done! If you press the music button and/or the sound button, you'll notice the effects turning on and off, just as intended. Now that we have handled MenuScene, let's go for every other location where we'll be playing the sound effects (and starting the music).

Tip

Alternatively, you can create a class that has methods for starting and stopping the background music, playing certain sound effects, and playing the button click, all using OALSimpleAudio.

Then you can locate and replace all the instances of OALSimpleAudio with your own custom class.

Handling MainScene sound

Since we have sound effects and music playing in just about every class of this game, we need to make sure they play only when the corresponding value is true.

So for starters, let's open MainScene.h and add a similar variable for a sound:

@interface MainScene :CCScene
{
  BOOL isSoundOn;
}

Next, in the init method, make sure you grab the values from NSUserDefaults:

Tip

Also make sure you set the isSoundOn variable in the init method before you attempt to play any sound effect. If you assign the value afterwards, you may experience unintended results.

-(id)init
{
    if ((self=[super init]))
    {
      //used for positioning items on screen
      winSize = [[CCDirector sharedDirector] viewSize];
    isSoundOn = [[NSUserDefaults standardUserDefaults] boolForKey:KeySound];

    ...
}
return self;
return self;
}

In MainScene.m, search for OALSimpleAudio, and go to every instance of it, adding the following if statement above it so that the sound effect plays only when that particular sound is enabled:

if (isSoundOn)
  ...

There should be two in playUnitCombineSound, one in goToMenu, one in restartGame, and one in moveUnit. Obviously, if you have more sound effects playing, then you should add them there as well, but these are the five that are present at this point.

Repeating for GameOverScene (and any other scenes)

It's basically the same as MainScene, so there won't be too much explanation. But all you really need to do is the following:

  • Create the isSoundOn variable in GameOverScene.h
  • Assign the value in the init method from NSUserDefaults
  • Add the if statement before every sound effect that gets played
  • Since this is your only other scene, move on to AppDelegate

Handling AppDelegate music

We need to make sure the music doesn't randomly start playing when users first load the game if they had decided to turn it off in a previous version.

So in AppDelegate.m, add the following if statement before the call to the playBgWithLoop method. Note that we don't need to store it in a variable because we're going to use it only once:

if ([[NSUserDefaults standardUserDefaults] boolForKey:KeyMusic])
    [[OALSimpleAudiosharedInstance] playBgWithLoop:YES];

Making sure that sound/music starts enabled

One of the things we want to make sure of is that the sound and music start as enabled when the user starts the game. By default, any Boolean in NSUserDefaults is false. Therefore, we need to make sure that both get set to true before the game starts, but only on the first time they run the game.

So in AppDelegate.m, at the very beginning of startScene, let's add code to check whether they've played before:

- (CCScene*) startScene
{
  //if they have not played before (in other words, first time playing)
  if (![[NSUserDefaults standardUserDefaults] boolForKey:@"hasPlayedBefore"])
  {
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:KeySound];
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:KeyMusic];
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"hasPlayedBefore"];
    
    [[NSUserDefaults standardUserDefaults] synchronize];
  }
  
  ...
  
  return [MainScene scene];//[CCBReader loadAsScene:@"MainScene"];
}

And that's it! It took a little bit of careful planning to make sure we handled every case that the user could run into, but that's the entire point of polishing your game—making sure that no matter what your user does or can do, the game responds appropriately and as intended.

Tip

The key takeaways from adding on/off settings for sound and music are as follows: storing the value in the NSUserDefaults, grabbing that value from a local variable in each scene, and using that variable to determine whether you should play a sound effect or not. If you want to be sure you've gotten all the instances, look up the project for OALSimpleAudio and go through all the classes you've created.

Make sure that you check for the variable for any future sound effects you add.

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

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