Adding SKShapeNodes - using while loop blanks UI

29 views Asked by At

I have a game which is running well but as soon as I introduce a while loop my entire UI goes blank.

I have some code which generates a sprite from an array and moves it down the screen

 func addALetter() {
        let randomX = CGFloat.random(in: 50...size.width - 50)
        let shrink = SKAction.scale(to: 0.1, duration: 0.01)
        let grow = SKAction.scale(to: 1, duration: 0.5)
        let wait = SKAction.wait(forDuration: 0.7)
        let spawn = SKAction.move(to: CGPoint(x: randomX, y: size.height - tileSize), duration: 0.001)
        let move = SKAction.moveTo(y: -500, duration: 7)
        let sequence = SKAction.sequence([shrink, spawn, grow, move, wait])
  
        // scroll through the lettersArray
        if activeLetter < lettersArray.count - 1 {
            bubbles[activeLetter].removeAllActions()
            bubbles[activeLetter].run(sequence)
            activeLetter += 1
        } else {
            // go back to first in letter array
            activeLetter = 0
            bubbles[activeLetter].removeAllActions()
            bubbles[activeLetter].run(sequence)
            
        }
    }

It is working fine triggered using an SKAction in my didMove to view run(SKAction.repeatForever(SKAction.sequence([SKAction.run(addALetter), SKAction.wait(forDuration: spawnTime)])))

but I have problems with that as I get to the end of the array because the action repeats too frequently making sprites disappear before I want them too.

So I tried using a while loop instead...

  while gameOn == true {
       addALetter()
        }

to repeat the action. But then I get a completely blank UI - I assume because it's then stuck in the loop there's no time to update the UI?

Looking for a solid way to repeat the function that I can vary the frequency as the array gets to low numbers

1

There are 1 answers

0
bg2b On

It seems likely that the problem is that spawnTime is small enough that you're wrapping around through the letters too quickly and start doing bubbles[activeLetter].removeAllActions() before the bubble's previous action sequence has finished.

I think the most best way to deal with this sort of situation is to coordinate through completion blocks. I.e., use the run method with a closure to be called after the action finishes, https://developer.apple.com/documentation/spritekit/sknode/1483103-run. That way you don't wind up trying to adjust delays explicitly in an effort to keep actions from being prematurely cancelled. The completion blocks will update state that you can use to coordinate, or they can run other actions directly.

It's not clear from your question what behavior you want. But, e.g., you might have addALetter set a flag for the bubble when it starts the action sequence and include a completion block for the sequence to clear the flag. Then before addALetter restarts the sequence for a bubble it can make sure that the flag is clear; if it's not (the bubble is still running the previous sequence), just return without doing anything.