Nurassyl Nuridin
Nurassyl Nuridin

Reputation: 464

My app crashes because of unfinished sounds in my sprite-kit game

Can't understand why my app crashes always going background when a sound is playing. If no sound here app won't crash. I have a SKAction which plays a sound. I preload this songs with static let in Actions class and call static func which returns SKAction with sound. So I have completion level sound. When it plays, I hit home button, them come back to app. There app crashes with message "Thread 1: EXC_BAD_ACCESS(code=1, adress=0xff8e9abc)". Has anybody an idea what's going on here?

This is my struct called Actions:

public struct Actions {
    static let completionSound = SKAction.playSoundFileNamed("CompletionSound.mp3", waitForCompletion: false)

    static func playCompletionSound() -> SKAction {
        return completionSound
    }
}

Here in my class called Grid, I have condition whether level completed or not:

class Grid {
    var delegate: GridDelegate?
    private var completed: Bool = false
    ...

    ...
    private func completeLevel() {
        if completed {
            let waitAction = SKAction.wait(forDuration: 1.5)
            let blockAction = SKAction.run {
                let groupAction = self.settings.sound ? SKAction.group([Actions.playGridSwishSound(), Actions.removeGridAction()]) : Actions.removeGridAction()
                self.gridNode.run(groupAction, completion: {
                    self.gridNode.removeAllChildren()
                    self.delegate?.completionLevel()
                })
            }
            let seq = SKAction.sequence([waitAction, blockAction])
            run(groupAction)
        }
    }

There I have a node, which shows SKLabelNode with "COMPLETED" text.

class CompletionNode: SKNode {
    private let completionLabel = SKLabelNode(fontNamed: MainFont)

    init(settings: Settings) {
        self.settings = settings
        super.init()
        isUserInteractionEnabled = false

        completionLabel.text = Titles.CompletedTitle
        completionLabel.fontSize = 46.0
        completionLabel.fontColor = GoldenColor
        completionLabel.centering()
        completionLabel.position = CGPoint(x: 0.0, y: 0.0)
        completionLabel.xScale = 0.0
        completionLabel.yScale = 0.0
        completionLabel.alpha = 0.0

        addChild(completionLabel)
    }

    func animate(completion: @escaping () -> ()) {
        let waitAction = Actions.waitAction(duration: 0.2)
        let scaleAction = SKAction.scale(to: 1.0, duration: 0.4)
        scaleAction.timingMode = .easeInEaseOut
        let fadeInAction = Actions.fadeInAction(duration: 0.4)
        fadeInAction.timingMode = .easeIn
        let groupAction = SKAction.group([scaleAction, fadeInAction])

        let llBlock = SKAction.run {
            self.completionLabel.run(groupAction)
        }

        let seq = SKAction.sequence([waitAction, llBlock])
        run(seq, completion: completion)
    }
}

In gameNode where I have grid instance, which is the delegate of last one, I add completionNode to the scene:

class GameScene: SKNode {
    var gird: Grid?
    let hudNode = HUDNode()
    let completionNode = CompletionNode()
    var completionOn = false
    ...
    init() {
        super.init()
        grid = Grid()
        grid!.delegate = self
        grid!.position = CGPoint(x: 0.0, y: 0.0)
        addChild(grid!)

        hudNode.position = CGPoint(x: 0.0, y: MainSize.height * 0.44)
        hudNode.zPosition = 40.0
        hudNode.delegate = self
        addChild(hudNode)

        completionNode.alpha = 0.0
        completionNode.position = CGPoint(x: 0.0, y: 0.0)
        completionNode.zPosition = 60.0
        addChild(completionNode)
    }

    internal func completeLevel() {
        addCompletionScene()
    }

    private func addCompletionNode() {
        run(Actions.playCompletionSound())
        completionNode.alpha = 1.0
        completionNode.animate {
            self.completionOn = true
            self.hudNode.isUserInteractionEnabled = true
        }
    }
}

When I reach the end of level I press home button, then come back to my game, after what crash happens.

Upvotes: 2

Views: 131

Answers (1)

Stoyan
Stoyan

Reputation: 330

It appears playSoundFileNamed has problems preserving the audio session while your app/game transitions between foreground and background mode.

Depending on your implementation, after the app resumes after the interruption you get unpredictable behavior ranging from muted playback to failed loading sound assets to plain crashing as in your case.

Here is a topic that discusses similar problems: SKAction playSoundFileNamed doesn't work after receiving two consecutive phone calls

I would suggest to stop using playSoundFileNamed actions and use AVAudioPlayer for playing sound files instead. It takes more effort into configuring it but it has been around for a long time and works flawlessly.

Upvotes: 1

Related Questions