9.8 C
New York
Sunday, March 9, 2025

ios – Lag between SKAction and SKAudioNode when SKScene is paused and resumed


Overview

In my SKScene, I’ve a SKAudioNode taking part in an audio file, declared as follows:

   var backgroundMusic: SKAudioNode?

and initialized like this:

    guard let musicURL = Bundle.principal.url(forResource: "MY_AUDIO_FILENAME", withExtension: "mp3") else { fatalError() }
    backgroundMusic = SKAudioNode(url: musicURL)
    backgroundMusic?.isPositional = false

For simplicity’s sake, take into consideration the audio performed as a voice counting loud from 1 to 1000:

“one, break, two, break, three, break, — [cut]”

Now, my objective is to have the audio taking part in and carry out stuff in my code when the audio reaches a particular half,, e.g. when the voice counts the quantity 45.
To attain that, I arrange a timer utilizing SKAction as follows:

    let wait = SKAction.wait(forDuration: 48) 
    let playAlarmSound = SKAction.run {
        // carry out stuff
    }
    let timer = SKAction.sequence([wait, playAlarmSound])

I add them each to the scene on the similar time, in order that in principle, when the audio reaches that particular level, the timer fires.

    run(timer, withKey: "Timer")
    addChild(backgroundMusic!)

Points

  1. Each time the SKScene is paused after which resumed, the SKAudioNode will leap a small portion of audio, within the instance case like a single quantity could be skipped circa.
  2. After every pause&resume, the timer will fireplace a bit later. That is evident within the pattern mission I present down under, the place should you pause&resume round 10 occasions, the timer will fireplace round quantity 50 as a substitute of 45.

Tried options

  1. Pausing the SKAudioNode individually each time the SKScene is paused, by saying backgroundMusic?.run(SKAction.pause()).
    Though this mounted problem 1. and reduces the lag of problem 2. by quite a bit, it’s nonetheless not an optimum resolution because the timer will nonetheless not be synchronized with the audio.
    This time tho, it is going to fireplace simply round a second sooner than when it’s imagined to, as a substitute of a number of seconds later.
  2. Utilizing Timer. That is NOT a viable resolution, for 2 causes: 1) it ignores a node’s, scene’s or the view’s paused state, as acknowledged by a number of solutions right here [sources: 1. 2 ] 2) I’ve tried it and it introduces lag into my sport – by expertise, for my part, when utilizing SpriteKit it is higher to not strive hybrid options.

I don’t know why this lag happens, so if anybody can shine a lightweight about it I might be grateful.

Furthermore, I am searching for a constant and optimum resolution for the problems described. I would like to have the ability to carry out stuff within the code precisely at a particular working time of the audio (which isn’t on completion).

Pattern Challenge

This can be a minimalistic pattern mission that highlights the problems I’ve identified. No .sks information are wanted, simply copy&paste and construct&run.

The mission begins the audio file and the timer routinely as quickly as GameScene is introduced. From there, you’ll be able to pause/resume the scene by tapping wherever on the purple display screen to breed the problems. With my settings and the audio I offered, the timer ought to fireplace when the rely reaches quantity 45.

Everytime you pause/resume the scene, and when the timer fires, the app will print on console.

Word that I used this resolution from one other query to maintain the paused state constant when the app goes to background.

For this pattern, I recommend to obtain and use this audio file (Dropbox hyperlink) of a man counting numbers, because it makes the problems evident.

remaining class GameViewController: UIViewController {

    override func viewDidLoad() {
        tremendous.viewDidLoad()
    
        let skView = SKView(body: view.body)
        view = skView
        skView.ignoresSiblingOrder = true
    
        let gameScene = GameScene()
        skView.presentScene(gameScene)
    }
}



import SpriteKit

remaining class GameScene: SKScene {

    var backgroundMusic: SKAudioNode?

    var isGamePaused: Bool = false {
        didSet {
            self.isPaused = isGamePaused
        
// Uncomment the next strains to check my resolution, which isn't optimum. After a number of pause/resume, the timer will nonetheless not be synchronized with the audio and can fireplace simply round a second sooner than when it's imagined to.
        
            //if isGamePaused {
            //   self.backgroundMusic?.run(SKAction.pause())
            //} else {
            //   self.backgroundMusic?.run(SKAction.play())
            //}
        }
    }
    override var isPaused: Bool {
        didSet {
            if (self.isPaused == false && self.isGamePaused == true) {
                self.isPaused = true
            
// Uncomment the next line to check my resolution, which isn't optimum. After a number of pause/resume, the timer will nonetheless not be synchronized with the audio and can fireplace simply round a second sooner than when it's imagined to.
            
              //self.backgroundMusic?.run(SKAction.pause())
            }
        }
    }

    override func didMove(to view: SKView) {
        tremendous.didMove(to: view)
        view.isMultipleTouchEnabled = false
        backgroundColor = .purple
    
        startAudioAndTimer()
    }

    func startAudioAndTimer() {
    
// ISSUE: On pause&resume, particularly after a number of occasions, the timer will get unsynchronized with the audio and can fireplace many seconds later than when it's imagined to.
    
        guard let musicURL = Bundle.principal.url(forResource: "Test_Song", withExtension: "mp3") else {
            fatalError("Error: add a mp3 audio file to the mission")
        }
        backgroundMusic = SKAudioNode(url: musicURL)
        backgroundMusic?.isPositional = false
    
        let wait = SKAction.wait(forDuration: 48) // With this worth for length, should you're utilizing the audio file I offered, the alarm ought to ring on the finish of '45' within the tune
        let playAlarmSound = SKAction.run {
            print("n--- The alarm rings ---n")
        }
        let timer = SKAction.sequence([wait, playAlarmSound])
    
        run(timer, withKey: "Timer")
        addChild(backgroundMusic!)
    }

    override func touchesBegan(_ touches: Set, with occasion: UIEvent?) {
        isGamePaused = !isGamePaused
        let statusString = isGamePaused ? "paused" : "resumed"
        print("nScene (statusString)n")
    }

    override func touchesMoved(_ touches: Set, with occasion: UIEvent?) { }

    override func touchesEnded(_ touches: Set, with occasion: UIEvent?) { }

    override func touchesCancelled(_ touches: Set, with occasion: UIEvent?) { }
}

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles