How to stop a audio SKAction?
Asked Answered
V

2

13

Goal: I want to present a new scene:

[self.scene.view presentScene:level2 transition:reveal];

and end the current background music in order to start the new background music (the new background music from level 2).

TheProblem: Upon presenting the new scene the background music of the 1. scene (level1) keeps playing and DOES not stop even when leaving the miniGame since the whole game consists of few mini games.

The music played is an SKAction:

@implementation WBMAnimalMiniGameLvL1

    -(id)initWithSize:(CGSize)size {    
        if (self = [super initWithSize:size])
        {
            /* Setup your scene here */
        self.temporaryScore = 0;
        self.animalSKSprites = [[WBMAnimalsMiniGameModel alloc] init];
        self.backgroundImage = [SKSpriteNode spriteNodeWithImageNamed:@"farmBackground1024x768.png"];
        self.backgroundImage.position = CGPointMake(CGRectGetMidX(self.frame),
                                       CGRectGetMidY(self.frame));
        self.temporaryStartingPointIndex = -1;
        [self addChild:self.backgroundImage];
        self.playBackgroundSound = [SKAction playSoundFileNamed:@"farm.mp3" waitForCompletion:NO];

        //SKAction *repeat = [SKAction repeatActionForever:playSound];
        [self runAction:self.playBackgroundSound withKey:@"play"];

        [self drawAllAnimalsOntoScreen];

    }
    return self;
}

Here does the transition to the next level happen:

-(void)transitionToNextLevel
{
    NSLog(@"transitionToNextLevel");
    SKTransition *reveal = [SKTransition moveInWithDirection:SKTransitionDirectionDown duration:0.5];
    //SKView *skView = (SKView *)self.view;
    SKScene *level2 = [[WBMAnimalMiniGameLvL2 alloc] initWithSize:self.size];

    level2.scaleMode = SKSceneScaleModeAspectFill;

    [self removeAllActions];
    [self removeActionForKey:@"play"];
    //self.scene.view.paused = YES;
    //self.playBackgroundSound = nil;
    [self.scene.view presentScene:level2 transition:reveal];
}

In the comments code lines you can se what I have already tried and what did not work. The :

[self removeAllActions];
    [self removeActionForKey:@"play"];

did absolutely nothing. The:

self.scene.view.paused = YES;

line stops only the transition but the music still continues.

I have tried the following: - used a weak or strong property on a :

@property (nonatomic,weak) SKAction *playBackgroundSound;

to secure a pointer to the SKAction so I can access it beside using the "withKey" property since I initialized the SKAction in "initWithSize". Someone wrote that SKAction is a fire-and-forget object which I understood as without having stored a pointer to it accessing it later is not possible (directly). However, it didnt work/help me.

I looked into many other stackoverflow posts and none helped me or at least gave me a clue why this happens:

SKAction playSoundFileNamed stops background music

Stop SKAction that RepeatsForever - Sprite Kit

Is it possible to end an SKAction mid-action?

Pausing a sprite kit scene

... Thoughts: It seems that the action is created with the creation of the SKScene object. It is "hooked" to it and finished after the audio duration. While I was using repeatForever it would never stop. However there is no way to pause or stop it. I also tried hooking the SKAction to SKSpriteNode. When the SKScene is loaded animals are loaded with it. So I tried hooking the SKAction on the SKSpriteNode and use removeAllActions and alike from the SKSpriteNode but it didnt work aswell.

I checked the documentation on SKAction,SKView,SKScene,SKSpriteNode but in the end it didnt help me much.

It looks like the object exists somewhere running its actions.

What the error is not about:

  • It is not simulator or device related error - I have tested it on both the simulator and device with the same result (error).

  • It is not a project related error - I have tested it in a separate project , much smaller and the with the same result (error).

A temporary solution: I have been using the AVAudioPlayer class from AVFoundation framework. I created a :

//@property (nonatomic) AVAudioPlayer *theBackgroundSong;

This didnt help much since I wanted to change theBackgroundSong when level2 was loaded and I had huge problems accessing the property from a nested SKScene structure.

Any advice,clue,hint or idea would be of great help.

Vestigial answered 21/3, 2014 at 15:9 Comment(4)
AVAudioPlayer or a "proper" audio engine like ObjectAL are the way to go. I believe the playSoundFileNamed: action merely exists just to do some quick audio (tests perhaps) but I wouldn't use it for any real-world application. Mainly because you get absolutely no control over the audio being played back, you can't pitch or pan it, pause or resume it, preload or release it.Acrefoot
I agree with you. It seems that I will have to change my approach. I wasn´t sure if the audio playback capabilities of iOS sprite kit were low or if I lacked the knowledge to use it.Vestigial
I think they put that action in there as a convenience and for beginners/learners. SK would seem missing without a barebones "play audio" feature.Acrefoot
I used it to play momentary short sounds hooked onto SKSpriteNodes like animal sounds. I did the job quite well. However, if the last more then few seconds it becomes a problem because if you click the animals faster then the sounds keep playing at the same time. I can not queue them or stop them. I belive apple will solve this slowly and add more functionality. For beginners its ok.Vestigial
T
14

You can't stop audio from playing when run from SKAction. You will need another engine to run your background music.
Try AVAudioPlayer - it is really easy and straightforward to use. Something like this will work:

First import header and add new property to your scene or view controller:

#import <AVFoundation/AVFoundation.h>

@interface AudioPlayerViewController : UIViewController {

@property (strong, nonatomic) AVAudioPlayer *audioPlayer;

}

After this in viewDidLoad or in didMoveToView method of scene add following code:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/audiofile.mp3", [[NSBundle mainBundle] resourcePath]]];

    NSError *error;
    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    self.audioPlayer.numberOfLoops = -1;

    if (!self.audioPlayer)
        NSLog([error localizedDescription]);                
    else 
        [self.audioPlayer play];

}

Each time you need your music to stop, just call [self.audioPlayer stop];

When you need new music to play, put a new player into the property initialized with your new music piece.

Hope this helps.

Thesis answered 23/3, 2014 at 11:48 Comment(3)
"You can't stop audio from playing when run from SKAction" - you are right. I was not sure, but now I am. It helped :).Vestigial
It didn't work for me, but looks like this is the way to go. But, I am not getting any sound out. Do you know what might be the problem? error is:The operation couldn’t be completed. (OSStatus error 2003334207.)Musicale
This is very annoying because AVPlayer stops Apple Music from running in the background. Hopefully Apple fixes this.Aspergillosis
S
2

Now you can stop playing sound action by assigning key string for it then using removeActionForKey later

Sorry, this is Swift. But it can be ported to Obj-C.

let action: SKAction = SKAction.playSoundFileNamed("sounds/boom.wav", waitForCompletion: true)
self.runAction(action, withKey:"die-sound")
// later you do:
self.removeActionForKey("die-sound")

I have tested successful

Slumber answered 15/10, 2015 at 18:56 Comment(4)
In Swift, awesome ;). Please edit only in which version (1.2 or 2.0)Vestigial
I've seen this solution to several similar questions in here, and it doesn't work for me. I can see the action is being successfully removed from the node, but the sound continues to play until it's finished.Valli
It does not work in Objective-C, even today. For me it doesn't work.Musicale
It doesn't works and looks like never worked, unfortunately.Borrell

© 2022 - 2024 — McMap. All rights reserved.