问题
I want to have a sound when a node is clicked. currently the code is:
let sndButtonClick = SKAction.playSoundFileNamed("button_click.wav", waitForCompletion: false)
and from touches began its
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touches: AnyObject in touches {
let location = touches.locationInNode(self)
if playButton.containsPoint(location) {
playButton.runAction(sndButtonClick)
playButton.removeFromParent()
let nextScene = GamePlayMode(size: self.scene!.size)
nextScene.scaleMode = self.scaleMode
self.view?.presentScene(nextScene)
}
}
I did the exact same thing for the collision of two nodes in gameplaymode and it works but in main menu it does not work!
I tried
self.runAction(sndButtonClick)
then
playbutton.runAction(sndButtonClick)
both didnt work
回答1:
Why running sound action on a button doesn't work ?
This is from the docs:
An SKAction object is an action that is executed by a node in the scene (SKScene)...When the scene processes its nodes, actions associated with those nodes are evaluated.
Means the actions for a node will run only if the node is added to the scene. So this has no effect :
playButton.runAction(sndButtonClick)
playButton.removeFromParent()
because you have removed the button from the scene and it will not be present in the next frame when actions should be executed. That is how runAction
method works:
Adds an action to the list of actions executed by the node...The new action is processed the next time the scene’s animation loop is processed.
Also, because you are immediately calling presentScene
there will be no next frame anyways, so even if you delete removeFromParent
statement, sound will not work, because there is no next frame.
Why running sound action on a scene doesn't work?
self.runAction(sndButtonClick)
won't work because you are making a transition immediately without waiting for a next frame where the queued actions will be executed (like described above).
Solution for the Problem
To play the sound before transition, you have to wait for a next frame, and you can do something like:
runAction(sndButtonClick, completion: {
self.view?.presentScene(nextScene)
})
or
let block = SKAction.runBlock({
self.view?.presentScene(nextScene)
})
runAction(SKAction.sequence([sndButtonClick, block]))
Preventing Leaks:
Consider using capture list inside of a block which captures self to avoid possible strong reference cycles when needed, like this:
let block = SKAction.runBlock({
[unowned self] in
//use self here
})
In this particular case of yours, it should be safe to go without capture list because scene doesn't have a strong reference to the block. Only block has strong reference to the scene, but after the block is executed, because nothing retains it (no strong references to it), it will be released, thus the scene can be released correctly. But, if the block was declared as a property, or the action which executes the block was running infinitely (using repeateActionForever
method to repeat a certain sequence), then you will have a leak for sure.
You should always override scene's deinit to see what is going on (if it is not called, something retaining the scene and causing the leak).
来源:https://stackoverflow.com/questions/35716994/sound-is-not-playing-when-node-is-touched-in-spritekit-swift