How to play audio at launch image in Cocos2D v3.x iOS? - ios

I'm making a game in Cocos2D iOS and would like play a short audio clip (in .m4a format) when the launch image (Default) is displayed.
I tried adding this line:
[[OALSimpleAudio sharedInstance] playBg:#"clip.m4a"];
in application didFinishLaunchingWithOptions but doesn't work.
I also tried playing a .caf file instead of .m4a, but it also doesn't play.
Can you guys please help me in this regard?
Thanks a lot!

Are you talking about the default loading screen that is defined in your project settings as 'default.png'? If yes, I don't think it's possible to play a sound at the same moment the app is launched, because it is currently loading it. What you could do however is a IntroScene that would only implement the onEnter method and immediately apply a transition to, example, your Main Menu. This is what I am doing right now in my game (using cocos2D 2.1, but I guess this isn't very different from a version to another) :
-(void) onEnter
{
[super onEnter];
CCScene *scene;
//Check if app has already launched once
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"]) {
scene = [MainMenuScene sceneWithParticles:nil];
}else{
scene = [Tutorial sceneWithParticle:nil];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
//PLAY YOUR SOUND HERE
//Transition
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:scene]];
}
Hope this helps! :)

Related

Multitask Issue in SpriteKit Game

I'm developing a game with Sprite Kit but I am having a multitasking issue. When I press the home button, during the game execution, my SKScene.paused becomes true, and I make the proper changes to the app in my applicationDidEnterBackground method at AppDelegate.m, such as saving stuff with NSUserDefaults. Anyway, if I opened my app again, it should resume from where it left off, but what happens is that my app terminates and starts again. This only happens in my iPhone (in the iOS Simulator it works fine). Since I am new at creating games with Sprite Kit and creating apps at all, I was hoping for some clue of what could the problem be...
PS: I think the problem is something about the app not being "suspended" correctly, because if I press the home button and immediately reopen the app, it works fine.
Here is my code:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if(self.scene == nil) return;
[self.scene saveUserDefaults];
[self.scene pausar];
self.scene.paused = true;
}
In MyScene.m, inside the initWithSize method:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.scene = self;
And in the AppDelegate.h file: #property (weak, nonatomic) MyScene * scene;
Now, the saveUserDefaults method, which is inside the MyScene.m file:
-(void) saveUserDefaults
{
[userDefaults setBool:true forKey:#"active"];
[userDefaults setInteger:highScore forKey:#"highScore"];
[userDefaults setBool:soundOn forKey:#"soundOn"];
[userDefaults setBool:musicOn forKey:#"musicOn"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Well, without seeing more of your code, it is impossible to know what is causing your issue.
First, read this question. Similarly, you should read up on the UIAppDelegate class since it is something you must know very well when programming on a phone where frequent interruptions like phone calls, etc can happen while someone is using your app.
More than likely, you have a problem with how you are responding to one of the protocol methods in the delegate.

How to end a sound right away?

I'm working on an IOS game, and the background music clip is 7 seconds long, so when the scene changes from one scene to the next, I want it to stop instantly, but it continues and finishes the 7 second loop then stops.
This is the code:
[self runAction:[SKAction repeatActionForever:[SKAction playSoundFileNamed:#"sound 1.m4a" waitForCompletion:YES]]];
if (_dead == YES) {
[self removeAllActions];
}
where _dead is when the player dies, and the new scene is triggered.
How can I get the music to stop that instant, as opposed to finishing its loop?
Instead of using SKAction to play your background sound file, I would suggest using AVFoundation which allows you to simply issue a stop command.
Update
I can't think of any food tutorials that focus primarily on the sound side. Most deal with video and sound. Try google with something like 'AVFoundation tutorial'.
To use AVFoundation you can do this for your purposes...
Add the AVFoundation.framework to your project.
In your .h file add the delegate <AVAudioPlayerDelegate>
In your .m file #import <AVFoundation/AVFoundation.h>
Create a property AVAudioPlayer *_backgroundMusicPlayer;
Create this method:
- (void)playBackgroundMusic:(NSString *)filename {
NSError *error;
NSURL *backgroundMusicURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
_backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:backgroundMusicURL error:&error];
_backgroundMusicPlayer.numberOfLoops = -1;
_backgroundMusicPlayer.volume = 0.2;
_backgroundMusicPlayer.delegate = self;
[_backgroundMusicPlayer prepareToPlay];
[_backgroundMusicPlayer play];
}
To start playing [self playBackgroundMusic:[NSString stringWithFormat:#"bgMusic.mp3"]];
To stop playing [_backgroundMusicPlayer stop];
That should do the trick. I suggest you read up on the AVAudioPlayer Class Reference so you understand its properties. For example, numberOfLoops set to -1 will loop the sound indefinitely until you call the stop method. Another issue to keep in mind is the audio file sound format. AVFoundation is somewhat picky about what sound files it will play. I always stick to mp3.
Fuzzygoat also makes an excellent point. I have not tried his approach in regards to sound so I do not know if it will solve your issue. It is similar to what you already have but with a slight change.
To start the sound:
SKAction *mySound = [SKAction repeatActionForever:[SKAction playSoundFileNamed:#"astroKitty 1.m4a" waitForCompletion:YES]];
[self runAction:mySound withKey:#"boogieDown"];
To stop the sound:
[self removeActionForKey:#"boogieDown"];

How to tell if MoviePlayerController has ever played?

I want to prevent a user from performing an action until they have at least pressed play on the MoviePlayerController. Would also be helpful to know if they have watched the video all the way through, or how far they have watched.
At some point register for a playback notification like so:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateChanged)
name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
Then, within playbackStateChanged, you can indicate that you have played the video with some BOOL:
- (void) playbackStateChanged {
if(moviePlayerController.playbackState == MPMoviePlaybackStatePlaying){
hasPlayed = YES; //BOOL value
}// reading the playback
}
You can set a variable called countOfTimesButtons. You can have an IBAction on the play button and when it is clicked, you can increase the countOfTimesButtons pressed. If the countOfTimesButtons is greater than 0, you can do something depending on your circumstance.
Hope this helps...
you can also save the state in a persistent way, so the user will not have to view the video at each application session:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"videoViewed"];
and elswhere in the program:
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"videoViewed"]) {
do stuff
}

How to mute/unmute audio when playing video using MPMoviePlayerController?

I've created my own custom controls for use with the MPMoviePlayerController. So far everything works except the mute button control.
I've configured the AVAudioSession using the following code before I create my instance of the MPMoviePlayerController.
NSError *modeError = nil;
[self.audioSession setMode:AVAudioSessionModeMoviePlayback error:&modeError];
if (modeError != nil) {
NSLog(#"Error setting mode for AVAudioSession: %#", modeError);
}
NSError *categoryError = nil;
[self.audioSession setCategory:AVAudioSessionCategoryPlayback error:&categoryError];
if (categoryError != nil) {
NSLog(#"Error setting category for AVAudioSession: %#", categoryError);
}
Then in my mute button callback method I have the following code:
NSError *activeError = nil;
[self.audioSession setActive:NO error:&activeError];
if (activeError != nil) {
NSLog(#"Error setting inactive state for AVAudioSession: %#", activeError);
}
When clicking the Mute button I get the following unuseful error:
Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)"
I am linking to the AVFoundation framework.
This is really starting to bug me as I can't for the life of me work out a way to reduce or mute the playback audio of my application.
I don't want to change the system global volume just the application level volume as defined by the AVAudioSession AVAudioSessionCategoryPlayback category.
It seems that you can set the volume of the AVAudioPlayer but not the MPMoviePlayerController. I've seen other posts here on SO that say just create an instance of AVAudioPlayer and set the volume but this just causes my app to crash and I expect it has something to do with the fact I'm not using the initWithContentsOfURL:error: or initWithData:error: and instead using `init'.
Any help would be appreciated.
After speaking to an Apple technician it turns out that it's not possible to control or mute the audio using MPMoviePlayerController.
Instead you have to create your own controller using AVFoundations AVPlayer class.
Once you're using that it's a matter of creating a custom audio mix and setting the volume level. It actually works very well.
Sample code:
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:[self localMovieURL] options:nil];
NSArray *audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
// Mute all the audio tracks
NSMutableArray * allAudioParams = [NSMutableArray array];
for (AVAssetTrack *track in audioTracks) {
AVMutableAudioMixInputParameters *audioInputParams =[AVMutableAudioMixInputParameters audioMixInputParameters];
[audioInputParams setVolume:0.0 atTime:kCMTimeZero ];
[audioInputParams setTrackID:[track trackID]];
[allAudioParams addObject:audioInputParams];
}
AVMutableAudioMix * audioZeroMix = [AVMutableAudioMix audioMix];
[audioZeroMix setInputParameters:allAudioParams];
// Create a player item
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem setAudioMix:audioZeroMix]; // Mute the player item
// Create a new Player, and set the player to use the player item
// with the muted audio mix
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
self.mPlayer = player;
[mPlayer play];
I've written an MPMoviePlayerController replacement class that adds support for volume level. I will upload the to github shortly and add the link in this post.
I know this is an old post, but I managed to find a way to successfully control the volume of the MPMoviePlayerController control in iOS6 & iOS7, using an MPVolumeView. One gotcha is that it does NOT work in the simulator, only on the physical device. For just controlling the volume, adding a hidden MPVolumeView will work fine. However if you use a hidden one, the native OS volume display that appears when you change the volume using the physical device volume buttons will still appear centre screen. If you want to prevent this, make sure your MPVolumeView is not hidden. Instead, you can give it a very low alpha transparency and place it behind other views, so the user can't see it.
Here's the code i've used:
MPVolumeView *volumeView = [[MPVolumeView alloc]initWithFrame:CGRectZero];
[volumeView setShowsVolumeSlider:YES];
[volumeView setShowsRouteButton:NO];
// control must be VISIBLE if you want to prevent default OS volume display
// from appearing when you change the volume level
[volumeView setHidden:NO];
volumeView.alpha = 0.1f;
volumeView.userInteractionEnabled = NO;
// to hide from view just insert behind all other views
[self.view insertSubview:volumeView atIndex:0];
This allows you to control the volume by calling:
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0];
But I was still getting the native OS volume display appearing the first time I would try to change the volume - on subsequent loads it did not show this display, so figuring it was something to do with the stage in the viewcontroller life cycle, I moved it from my viewDidLoad method to the viewDidAppear method - it worked - the volume muted and the native volume display did not appear, but I now was able to hear a split second of audio before the video started playing. So I hooked into the playback state did change delegate of the MPMoviePlayerController. In viewDidload I added:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(videoPlaybackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
And the delegate callback method:
-(void)videoPlaybackStateDidChange:(NSNotification *)aNotification
{
// Note, this doesn't work in simulator (even in iOS7), only on actual device!
if ([moviePlayerController playbackState] == MPMoviePlaybackStatePlaying)
{
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0];
}
}
This muted the audio of the video before it started playing, but after the viewDidLoad in the life cycle, so no native OS volume muted display.
In my app, I retrieved and stored the current volume level before muting (using [MPMusicPlayerController applicationMusicPlayer].volume property), and then restored the volume to this level when the view controller was closed, meaning the user would be unaware that their device volume level was modified and reverted.
Also, if your MPMoviePlayerController is using a non-standard audio route in iOS7, calling [[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0] may not work for you - in this case you can loop through the subviews of your MPVolumeView control until you find a view which subclasses UISlider. You can then call [sliderView setValue:0 animated:NO] which should work for you. This method isn't using any private Apple APIs so shouldn't get your app rejected - after all there are so many legitimate reasons why you would offer this functionality, and it was possible in iOS6 without having to go to these lengths! In fact, I was bamboozled to discover that Apple had removed the functionality to set the volume on MPMoviePlayerController in iOS7 in the first place.. enforced migration to AVPlayer?
Update: My iPad app has now been approved using this method and is live on the app store.

AVAudioPlayer setVolume - How?

Does anyone know the best way to mute game sounds on my app.
I am currently using SystemSoundID which I know cannot have the volume adjusted and need to move to AVAudioPlayer.
Ideally I need a settings page where the volume can be changed and the volume level saved so when coming back out or leaving and re-opening the app it remembers to volume level?
I have tried it various ways using AVAudioPlayer but had no success at present and am receiving some bad reviews for my app for people saying they hate that they cant control the game volume... HELP!!
for AVAudioPlayer you can use this code to mute volume
in .h file
#interface ViewController : UIViewController
{
BOOL muted;
}
and in .m file
- (void)viewDidLoad
{
[super viewDidLoad];
muted = NO;
}
- (IBAction)speakerOnOff:(id)sender
{
if (muted) {
muted = NO;
[player setVolume:1.0];
} else {
muted = YES;
[player setVolume:0.0];
}
}
this code is from this great answer.

Resources