0

I'm trying to change the timeInterval in a scheduledTimer. I'm trying to do this by changing a variable to the interval and than setting the timeInterval to this variable. I don't get any errors but the timeInterval won't change. Can someone help me?

var enemyTimer = Timer()
var playTime = 0
var enemySpawnTime: Double = 3
enemyTimer = Timer.scheduledTimer(timeInterval: Double(enemySpawnTime), target: self, selector: #selector(GameScene.enemySpawn), userInfo: nil, repeats: true)
playTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(GameScene.ingameTimer), userInfo: nil, repeats: true)
    func enemySpawn() {

    let enemy = SKSpriteNode(imageNamed: "Enemy")

    let minValue = self.size.width / 8
    let maxValue = self.size.width - 20

    let spawnPoint = UInt32(maxValue - minValue)

    enemy.position = CGPoint(x: CGFloat(arc4random_uniform(spawnPoint)), y: self.size.height)

    let action = SKAction.moveTo(y: -70, duration: 5)
    enemy.run(action)

    self.addChild(enemy)
}

func ingameTimer() {

    playTime += 1

    if(playTime >= 10 && playTime < 30){
        enemySpawnTime = 2
        print(enemySpawnTime)
    }else
    if(playTime >= 30 && playTime < 60){
        enemySpawnTime = 1
        print(enemySpawnTime)
    }else
    if(playTime >= 60 && playTime < 120){
        enemySpawnTime = 0.75
        print(enemySpawnTime)
    }else
    if(playTime >= 120 && playTime < 180){
        enemySpawnTime = 0.5
        print(enemySpawnTime)
    }else
    if(playTime >= 180 && playTime < 240){
        enemySpawnTime = 0.25
        print(enemySpawnTime)
    }
}

I hope someone can help me! Thanks!

steff feyens
  • 127
  • 11

1 Answers1

1

The reason why your code doesn't work is because the Timer object doesn't know that its interval needs to be in sync with your enemySpawnTime. The solution is simple, just recreate the timer when you change the enemy spawn time.

But...

You should NEVER use Timer (NSTimer prior to Swift 3) or GCD to delay stuff when you're using SpriteKit. See this for more details.

The correct way to do this is to create a sequence of SKActions.

Assuming self is a subclass of SKScene, you can do this:

override func didMove(to view: SKView) {
    let waitAction = SKAction.wait(forDuration: enemySpawnTime)
    let enemySpawnAction = SKAction.run { self.enemySpawn() }
    let sequence = SKAction.sequence([waitAction, enemySpawnAction])
    somePlaceholderNode.run(SKAction.repeatForever(sequence))
}

where somePlaceholderNode is just a node that does nothing but run the action. I'll explain this later.

And you should do this for the other timer as well.

Now whenever you change the timer interval, also do this:

somePlaceholderNode.removeAllActions()
let waitAction = SKAction.wait(forDuration: enemySpawnTime)
let enemySpawnAction = SKAction.run { self.enemySpawn() }
let sequence = SKAction.sequence([waitAction, enemySpawnAction])
somePlaceholderNode.run(SKAction.repeatForever(sequence))

Here I first remove the action that the node was running, and tell it to run almost the same action, but with a different time interval. You can add this block of code to the didSet block of enemySpawnTime:

var enemySpawnTime: Double = 3 {
    didSet {
        let waitAction = SKAction.wait(forDuration: enemySpawnTime)
        let enemySpawnAction = SKAction.run { self.enemySpawn() }
        let sequence = SKAction.sequence([waitAction, enemySpawnAction])
        somePlaceholderNode.run(SKAction.repeatForever(sequence))
    }
}

Now your code should work!

The reason why we want a placeholder node here is because when we remove the action by calling removeAllActions, we don't want to remove all the actions that is running.

Community
  • 1
  • 1
Sweeper
  • 213,210
  • 22
  • 193
  • 313