Has
Has

Reputation: 885

Multiple NSTimers in spritekit not working as expected

Aim:

I have particles falling from the top of the screen and I would like them to increase their speed, as the score reaches a certain value.

Previous failed attempt:

I first made those falling particles affected by gravity and tried to increase the value of gravity as the score hit a certain value. However it didn't seem to work so I assume that gravity cannot be changed during the game.

Current situation:

After some research, I thought of using two global NSTimers, slowTimer and fastTimer. slowTimer is called in didMoveToView. In touchesBegan, everytime there is a tap, I call a function, changeSpeed which is this :

    func changeSpeed()
        {

            if score >= 2

            {
                slowTimer.invalidate()
                makeWallsDividerT = 300

                fastTimer =  NSTimer.scheduledTimerWithTimeInterval(1.4, target: self, selector: Selector("makeWalls"), userInfo: nil, repeats: true)
            }

        }

makeWallsDividerT is a variable in the function makeWalls which is called by the fastTimer.

Problem with current situation :

When it is game over and everything is reset, I do fastTimer.invalidate() and still there seem to be problems as it is not resetting to slowTimer. Also it seems that both timers are on as after reset, the game is much faster than fastTimer.

Any pointers please? Or any other ways of achieving what I am trying to do? Thanks!!

Upvotes: 3

Views: 135

Answers (4)

Whirlwind
Whirlwind

Reputation: 13675

Because the spawning action is simple eg. wait + add walls, you could just modify the speed of the spawning action, like this:

import SpriteKit

class GameScene: SKScene {

    var score = 0 { didSet{changeSpawningSpeed() } }

    override func didMoveToView(view: SKView) {
        /* Setup your scene here */

        let wait = SKAction.waitForDuration(2.0)
        let block = SKAction.runBlock({ [unowned self] in self.spawnWalls()})
        let sequence = SKAction.sequence([wait, block])

        runAction(SKAction.repeatActionForever(sequence), withKey: "spawning")
    }

    func spawnWalls(){print("Walls spawned")}

    func changeSpawningSpeed(){

        if score == 2 {
            if let action = actionForKey("spawning"){action.speed += 1.0}
        }
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        score += 1
    }
}

Upvotes: 2

Knight0fDragon
Knight0fDragon

Reputation: 16837

To rework your code so that you are no longer using NSTimers, you want to do the following:

override func didMoveToView(view:SKView)
{
    let spawnWall = SKAction.runBlock({[unowned self] in self.makeWalls();});
    let sequence = SKAction.sequence([SKAction.waitForDuration(3),spawnWall])
    let repeatForever = SKAction.repeatActionForever(sequence);
    self.runAction(repeatForver, withKey:"makeWalls"); 
}

func changeSpeed()
{
    if score >= 2
    {
        self.removeActionForKey("makeWalls");  
        makeWallsDividerT = 300;
        let spawnWall = SKAction.runBlock({[unowned self] in self.makeWalls();});
        let sequence = SKAction.sequence([SKAction.waitForDuration(1.4),spawnWall])
        let repeatForever = SKAction.repeatForever(sequence);
        self.runAction(repeatForver, withKey:"makeWalls"); 

    }
}

This will prevent multiple actions being fired. (In your case, you had multiple timers being fired, and you only stopped the last timer made.)

Upvotes: 2

Patrick
Patrick

Reputation: 1665

Also an alternative to the NSTimer is to use SKActions.

SKAction *wait = [SKAction waitForDuration:1.0f]; 

Upvotes: 2

hamobi
hamobi

Reputation: 8130

The problem is you shouldn't be using NSTimers in SpriteKit at all.

Use SKAction:

    runAction(
        SKAction.sequence([
            SKAction.waitForDuration(1.4),
            SKAction.runBlock({
                self.makeWalls()
            })
        ])
    )

Upvotes: 2

Related Questions