-2

I have set a timer in my code below. How can I make the timer reset every time it reaches a given duration say, 7 seconds?

class SpriteGroup    {
var sprites : [Sprite]
var isVisible : Bool
var startTime: TimeInterval = 0.0
var currentTime: TimeInterval = 0.0


init(sprites : [Sprite], isVisible: Bool, pos: CGPoint) {
    self.sprites = sprites
          ...       
    startTime = Date.timeIntervalSinceReferenceDate
    Timer.scheduledTimer(timeInterval: 1,
                         target: self,
                         selector: #selector(self.advanceTimer(timer:)),
                         userInfo: nil,
                         repeats: true)
}

   func advanceTimer(timer: Timer)
{
    currentTime = Date.timeIntervalSinceReferenceDate - startTime
}
Bane
  • 341
  • 1
  • 17
  • Why are you using Timer? Do you really need to use Timer? vs. using SpriteKit's built in method of handling time in the Update func – Ron Myschuk Mar 24 '17 at 19:46
  • @RonMyschuk I have a lot of complex actions in my code. That's why the `SpriteGroup` has the `currentTime` property. I need to be able to monitor each group of sprites and interrupt their actions occasionally. Based on this I believe Timer would work better for me. – Bane Mar 24 '17 at 20:14
  • 1
    Fair enough... but you haven't listed any reason that the built in update timer wouldn't handle. My philosophy is why introduce another set of objects when you already have the tools running on the scene? You may want to look at this link as well http://stackoverflow.com/questions/23978209/spritekit-creating-a-timer – Ron Myschuk Mar 24 '17 at 20:22
  • @RonMyschuk all the approaches in the link are using actions. I will still need the timer to persist even when occasionally I stop running all actions on the sprites. – Bane Mar 25 '17 at 12:45
  • @RonMyschuk ...that's why I am running the timer on the `SpriteGroup` instead of the sprites themselves – Bane Mar 25 '17 at 15:11
  • 1
    I think that you are misunderstanding the point of that link. All that link is saying is don't use timer, the fact that it uses actions is irrelevant. Update is always running, it never stops (unless you pause the scene) in fact it fires around 60 times per second. You say the link isn't valid because the examples are using actions, but then you talk about "stop running all actions" ...I'm confused – Ron Myschuk Mar 25 '17 at 15:13
  • It makes no difference where you are running the timer, i'll update my answer to show you an example of how the update statement can effect "time" in a subclass of sprites – Ron Myschuk Mar 25 '17 at 15:16

1 Answers1

1

I would probably do it like this instead

I've added a subclass of Clouds to show you how their Timer is affected by the update of the Scene, it just scrolls clouds across the scene at at an even pace regardless of device performance

private var lastUpdateTime: TimeInterval = 0.0

override func update(_ currentTime: TimeInterval) {

    //check if game is actually playing, don't want to update time if game is paused
    if lastUpdateTime == 0 || gameState != .playing {

        lastUpdateTime = currentTime
        return
    }

    let delta = currentTime - lastUpdateTime

    if delta > 7 {
        //this has now been 7 seconds so do something here
    }

    //reset timer
    lastUpdateTime = currentTime

    enumerateChildNodes(withName: "cloud*", using:  { cloud, stop in
        if let cloud = cloud as? Cloud {
            cloud.update(delta: delta)
        }
    })
}

class Cloud: SKSpriteNode {

    private var minY: CGFloat = 0
    private var maxY: CGFloat = 0
    private var cloudWidth: CGFloat = 0

    private override init(texture: SKTexture?, color: UIColor, size: CGSize) {
        super.init(texture: texture, color: color, size: size)
    }

    convenience init() {

        let texture = SKTexture(image: #imageLiteral(resourceName: "cloud1"))
        self.init(texture: texture, color: SKColor.white, size: texture.size())
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    func setup(type: CloudType) {

        texture = type.texture
        size = (texture?.size())!
        setScale(2.0)

        var midHeight = gameModel.gameHeight / 2
        if let anchorY = (self.parent as? SKScene)?.anchorPoint.y {
            midHeight = gameModel.gameHeight * anchorY
        }

        self.minY = gameModel.gameHeight * 0.3 - midHeight
        self.maxY = gameModel.gameHeight * 0.9 - midHeight
        cloudWidth = self.size.width

        let randomY = RandomFloatBetween(min: minY, max: maxY)
        self.position = CGPoint(x: gameModel.gameWidth / 2 + cloudWidth, y: randomY)
    }

    func update(delta: TimeInterval) {

        let speedX = CGFloat(delta) * 90
        self.position.x -= speedX

        if self.position.x <= 0 - (gameModel.gameWidth / 2 + cloudWidth) {
            resetCloud()
        }
    }

    private func resetCloud() {

        self.position.x = gameModel.gameWidth / 2 + cloudWidth
        self.position.y = RandomFloatBetween(min: minY, max: maxY)
        enabled = false
        run(.wait(forDuration: 5, withRange: 3), completion: { self.enabled = true })
    }
}
Ron Myschuk
  • 6,011
  • 2
  • 20
  • 32
  • If I understand you well, `Cloud` is of type `SKSpriteNode` so you can use `enumerateChildNodes(..)` in the game Scene but `SpriteGroup` (as shown above) is not of type `SKSpriteNode` how will I update this in the game Scene? – Bane Mar 25 '17 at 15:59
  • Just make it a subclass of SKSpriteNode, it won't hurt – Ron Myschuk Mar 25 '17 at 16:00
  • Really, after making it a subclass of SKSpriteNode I got a series errors. I managed to solve some of them, but currently I am not sure to get this one resolved. I added ` super.init(texture: SKTexture!, color: UIColor, size: CGSize) { super.init(texture: texture, color: color, size: size) } ` to my init (in the question) then I got the error `Cannot invoke 'SKSpriteNode.init' with an argument list of type ‘(texture: SKTexture!.Type.color:UIColor.Type, size:CGSize.Type, ()->())’` – Bane Mar 25 '17 at 17:00
  • Also is subclassing SKSpriteNode the best approach? I foresee issues in the future with this, always having to remind myself that though it's an `SKSpriteNode`, I am not using it per say as a `SKSpriteNode`. – Bane Mar 25 '17 at 17:05
  • Add this line to your init 'Super.init(texture: nil, color: .clear, size: CGSize.zero)' – Ron Myschuk Mar 25 '17 at 17:33
  • I honestly makes no diff if it is subclassed to SKSpriteNode, I use it all the time. This question has gotten off scope and I honestly don't feel that you are open to this route, so you should just go back to using timer. I've been using SpriteKit for 3 years and have developed over 20 games using it. My advice when I give it is only to help people get on the correct path as I know it. You are giving very little context regarding your sprites with your question and for the little code you did supply I gave you the proper solution, use it or not. Best of luck on your game! – Ron Myschuk Mar 25 '17 at 17:44