1

I'm working on a game with some monsters of different kinds (one kind = one subclass of SKSpriteNode) crossing the scene. To each kind is given a specific sound.

One example is I can have at a specific point in time:

  • 5 monsters A
  • 2 monsters B
  • 0 monster C

What I would like at any time, is to loop a sound for each class which is part of the scene (A, B) (and not for each sprite !) , and to stop playing a sound for each class absent from the scene(C).

My first idea was to store a specific sound into each class of monster and to add a SKAction to each sprite that would play loop the sound of its class. But this would play loop as many sounds as sprites on scene, and this doesn't match with my expectations.

Do you have an idea on the best approach to adopt for this ? Is there a possibility to implement a kind of observer that would be able to notify when an instance of class is on the scene and when it is not ?

Many thanks for your help !

Laurent Maquet
  • 400
  • 1
  • 4
  • 14

2 Answers2

1

As @Knight0fDragon said, "SKAction is not designed to play looped music"

Maybe you can do: (I diden't test it, now I'm on windows machine)

Class MonsterA : SKSpriteNode {

    static var monsterAInScene = Set<Monster>()
    static let audioAction: SKAction = SKAction.playSoundFileNamed("MonsterA-audio") //Change to AVAudio for loop
    static let audioNode: SKNode()

    init (scene:SKScene) {
          //Your init
          if let MonsterA.monsterAInScene.count == 0 {
             //play audio
             scene.addChild(MonsterA.audioNode)
             MonsterA.playAudio()
          }

          MonsterA.monsterAInScene.insert(self)
    }

    static func playAudio() {
         //Play audio code
         if MonsterA.audioNode.actionFroKey("audio") != nil {return}
         MonsterA.audioNode.runaction(MonsterA.audioAction, forKey "audio")

    }
    override removeFromParent() {
        MonsterA.monsterAInScene.remove(self)
        if let MonsterA.monsterAInScene.count == 0 {
            //stop audio code
            MonsterA.audioNode.removeActionWithKey("audio")
            MonsterA.audioNode.removeFromParent()
        }
        super.removeFromParent()
    }
}

@Knight0fDragon and @Alessandro Ornano are more expert than me, they can suggest if is the best way to do.

Simone Pistecchia
  • 2,746
  • 3
  • 18
  • 30
  • We had a conversation with Knight0fDragon about audio. You can find it here : http://stackoverflow.com/a/38408288/1894067 . IMHO AVAudioFoundation can still give a lot to gaming, but if your project start to iOS 9.x version I preefer to use , in agreement with Knight0fDragon, the SKAudioNode. But I have never tried looped music through SKAction. – Alessandro Ornano Sep 01 '16 at 14:18
  • I made a custom class SKAudio, the SKAudioNode still crash when the app goes through inactive to active status, the pause on SKAudioNode seems to have a bug. [link](http://stackoverflow.com/questions/37515284/skaudionode-crash-avaudioplayernode-mm333-start-required-condition-is-false) – Simone Pistecchia Sep 01 '16 at 14:41
  • @SimonePistecchia AHHH is that your issue? Pause is broke as of iOS 9 for SKNode (for some reason, it changes the pause state of all of its children) What you need to do is extend all of the possible SKNode subclass, then override pause, remember the pause state of the children, change pause, then reset the pause state of the children. It is a real pain to implement. Also, you cannot use SKNode, you would need to subclass a special version of this to handle pause (Unless somebody can tell me how to override pause on SKNode) – Knight0fDragon Sep 01 '16 at 18:13
  • @All Thanks for your time and your ideas. I'm working on it now ! – Laurent Maquet Sep 01 '16 at 20:09
  • @Knight0fDragon Thanks for the trick. But at this point is better to use AVAudio instead subclass all my nodes right? – Simone Pistecchia Sep 02 '16 at 08:47
  • No, you have to write extensions (only sknode needs a subclass) for everything or else you can never use pause on any SKNode whatsoever – Knight0fDragon Sep 02 '16 at 11:16
1

I have finally adopted SKAudioNode as advised bu KnightOfDragon.

At the beginning of the GameScene class, I have added an observer to check the actual number of monster of each kind (Here with kind A):

var monsterASound = SKAudioNode()

var currentNumberOfMonsterA = 0 {
    didSet {
        if currentNumberOfMonsterA > 0 {
            monsterASound.runAction(SKAction.play())
        } else{
            monsterASound.runAction(SKAction.stop())
        }
    }
}

currentNumberOfMonsterA is updated each time I add/remove a monster

//Add a monster A
self.currentNumberOfMonsterA += 1

//Removing a monster A
self.currentNumberOfMonsterA -= 1

//Destroying a monster A
self.currentNumberOfMonsterA -= 1

Then in the didMoveToView, I have added the SKAudioNode to the scene

self.monsterASound = SKAudioNode(URL: NSBundle.mainBundle().URLForResource("monsterA", withExtension: "wav")!)
self.monsterASound.runAction(SKAction.stop())
addChild(helicoSound)
halfer
  • 19,824
  • 17
  • 99
  • 186
Laurent Maquet
  • 400
  • 1
  • 4
  • 14