5

I can't seem to find much information about SKAudioNode. How do i play a sound only once? I do not want to repeat the sound.

What i am trying to accomplish is to play a short laser sound each time a bullet spawns, in spritekit.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Glutch
  • 662
  • 1
  • 6
  • 19

2 Answers2

14

Unfortunately, what @KnightOfDragon says is not correct (but I do not have enough reputation to comment).

SKAudioNode has been introduced in iOS 9 and is meant as a replacement for SKAction.playSoundFileNamed(...) as it is much more powerful (You can add it as a child to a SpriteKit SKNode and if the attribute positional is set to true, 3D audio mixing is added automatically).

In order to play a sound once with SKAudioNode use the following code:

if #available(iOS 9, *) {
    let pling = SKAudioNode(fileNamed: "pling.wav")
    // this is important (or else the scene starts to play the sound in 
    // an infinite loop right after adding the node to the scene).
    pling.autoplayLooped = false
    someNode.addChild(pling)
    someNode.runAction(SKAction.sequence([
        SKAction.waitForDuration(0.5),
        SKAction.runBlock {
            // this will start playing the pling once.
            pling.runAction(SKAction.play())
        }
    ])
}
else {
    // do it the old way
}

Update

Have a look here, how to use Actions on audio nodes: SKAction->Audio-Stuff. The documentation says:

Use SKAction playSoundFileNamed:waitForCompletion: only for short incidentals. Use AVAudioPlayer for long running background music.

This does not mean, that a SKAudioNode should not be used for one-time audio. With playSoundFileNamed you cannot change the volume or pause/stop playback and so on.

Juangamnik
  • 156
  • 6
  • There are differences with `SKAudioNode` and `playSoundFileNamed`. `SKAudioNode` is not a replacement for `playSoundFileNamed`. It is a replacement to using the `AVFoundation` on your game. `SKAction.playSoundFileNamed` goal is to play a basic sound that has little overhead to it, this is why you should use this action to play it as a soundeffect – Knight0fDragon Jan 04 '16 at 15:38
  • 2
    @Knight0fDragon: I disagree since `playSoundFileNamed` does not have positional audio or any other more sophisticated option and was meant for easy one-shot-audio without any control. `AVFoundation` on the other hand is more complex to use and not as easy to integrate into SpriteKit. `SKAudioNode` is an easy to use way to integrate `AVFoundation` into SpriteKit (as it uses `AVFoundation` behind the scenes). But more importantly I do not think, that `SKAudioNode` is primarily for music, as you can use it for positional audio, which is not likely to be used that much for music. – Juangamnik Jan 05 '16 at 16:57
  • how is that disagreeing with me, positional audio is an advanced feature that is not seen in many mobile games, I am not saying that SKAudio should never be used for sound effects, just that the playsoundfilenamed should be used first, due to the low overhead that is involved (like a short laser sound) – Knight0fDragon Jan 05 '16 at 16:59
  • 1
    @Juangamnik Not sure if you've ever run into this issue, but SKAudioNode is broken at the moment of speaking (headphones bug). Read more here http://stackoverflow.com/q/35662139/3402095 – Whirlwind Mar 08 '16 at 21:46
  • 1
    @Whirlwind Thank you very much, so your solution is to use AVAudioPlayer directly. Right? For my background music I use AVAudioPlayer anyway as SKAudioNode has the disadvantage that you cannot use it during the whole game (including scene transitions) AFAIK. I use SKAudioNode for effects, only. This is why I want to play a sound only once. – Juangamnik Mar 10 '16 at 09:23
  • 1
    @Juangamnik Personally, I use AVAudioPlayer when I need control over the sounds, like for background music, or loops that might need pausing at random moments (if SKAudioNode was not broken, I would use it instead for those loops). Otherwise, means for short sounds, I use `SKAction.playSoundFileNamed` which is unfortunately, also [buggy](http://stackoverflow.com/questions/32507946/skaction-playsoundfilenamed-doesnt-work-after-receiving-two-consecutive-phone-c). – Whirlwind Mar 10 '16 at 11:54
  • 1
    Also, I would probably use `SKAudioNode` even for short sounds if I need any kind of control over them which `SKAudioNode` offers, as long as I can maintain good performance ... – Whirlwind Mar 10 '16 at 12:02
  • Serious question: If you look at the Apple docs page for SKAction play(), how did you figure out that this was the syntax to use it: `pling.runAction(SKAction.play())` – Confused Oct 30 '16 at 14:40
  • @Confused: `SKAction` has several *class*-methods for creating new actions. So `SKAction.play()` creates a new action, that -- as soon as it is executed by the node -- will play the node. This is the general approach of actions. Such an API is often called internal DSL (domain-specific language) as you have an action language in Swift/Objective-C, which you can write within the respective host language. You create some actions and let a node run them. I hope I do not tell you something you already knew ;). – Juangamnik Dec 04 '16 at 15:52
  • Not at all. I'm using this syntax, but I still can't "reverse engineer" it to the Apple docs, to see how the Apple docs say - _this is how you use this_. there is a good chance I'm missing the mental tools to understand how to interpret the spartan Apple docs. – Confused Dec 04 '16 at 16:49
  • @Confused: If you read the overview of Apple's doc to [SKAction](https://developer.apple.com/reference/spritekit/skaction) the overall usage of actions is described: You create them with the aforementioned class methods and tell a node to execute them (via `run(_:)`). My code example is Swift 2.x. In Swift 3.x you would write `pling.run(SKAction.play())` instead, since they changed a lot of names (made them more consistent). – Juangamnik Dec 05 '16 at 17:26
  • I have read the docs. My question is very specific. How do you go from what's in the docs, to the correct syntax to actually use the action? How do YOU, specifically, join the dots between the docs and final, correct, working syntax? – Confused Dec 05 '16 at 18:23
  • 1
    Perhaps you should read the Swift book first (its free) to learn the basic syntax of swift, which is based on C/Java syntax – Juangamnik Dec 07 '16 at 12:09
6

if you are trying to do sound effects, you use SKAction.playSoundFileNamed(...) on the sprite that is creating the effect. SKAudioNode is more for having music playing in your game

Example:

//we have ship as an SKSpriteNode

//lets fire laser

ship.runAction(SKAction.playSoundFileNamed("pewpewpew.caf",waitForCompletion:false));
Knight0fDragon
  • 16,609
  • 2
  • 23
  • 44
  • Thank you! That's awesome, very simple. How would i pre-load the sound? Right now the game freezes for 0.1 second the first time the sound fires – Glutch Dec 18 '15 at 15:38
  • 1
    I believe you can save the action in its own variable at load time `let pewpew = SKAction.play....`, then just call `ship.runAction(pewpew);` – Knight0fDragon Dec 18 '15 at 15:50
  • [11/9/16] Dev Env: iOS 9+ and xCode 8 There is a known memory leak using this call. On the Apple Forum, you will see a post about the memory leak back in April of 2016, and has yet to be fixed. – cbuck12000 Nov 09 '16 at 14:04
  • @cbuck12000, can you post a link? I like to test these bugs to verify if it is user error or indeed system error – Knight0fDragon Nov 09 '16 at 14:17
  • @Knight0fDragon - https://forums.developer.apple.com/thread/20014 I verified that it is also in the current OS and with Xcode 8.0. I have not checked Xcode 8.1 – cbuck12000 Nov 12 '16 at 01:34
  • But then you can't control the volume of the playback. – Kirby Todd Jan 06 '22 at 04:58
  • @KirbyTodd i haven’t touched this stuff in years, but i believe you can control the volume prior to playback – Knight0fDragon Jan 06 '22 at 05:00