Touch duration in SpriteKit

209 Views Asked by At

I am currently working with SpriteKit and I want to run a code block in the update-loop as long as the user is touching a certain SpriteNode. I tried to achieve this by using a Boolean, that gets set to true, when the touchesBegan() method recognises a touch on this node and gets set to false, when the touchesEnded() method recognises a touch ending on this node. However, when the user touches the node and then moves his finger outside of the boundaries, the touchesEnded() method does not recognise this.

Is there a simple way to check if a touch, that began in this node, but then moved outside of it, still exists? Or can I check in general if a UITouch instance still exists?

1

There are 1 best solutions below

0
bg2b On

It's not clear what behavior you want, but in general you probably want to use touch identity to track what's happening.

For example, if you're handling touches in the scene containing the node, and if the desire is simply to have the action start when the node is touched and stop when that touch ends, then something like:

// Whatever touch initiated the action
var activeTouch: UITouch?
// Checked by update loop
var doingSomething = false

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  for touch in touches {
    // Ignore new touches if one is already active
    guard activeTouch == nil else { return }
    let location = touch.location(in: self)
    let touchedNodes = self.nodes(at: location)
    for node in touchedNodes {
      if <some test for the interesting node> {
        activeTouch = touch
        doingSomething = true
      }
    }
  }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  for touch in touches {
    if touch == activeTouch {
      // Finished
      activeTouch = nil
      doingSomething = false
    }
  }
}

If you also want the action to stop if the user moves their finger off the node and restart if they move back on, then also override touchesMoved, e.g.:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
  for touch in touches {
    if touch == activeTouch {
      let location = touch.location(in: self)
      let touchedNodes = self.nodes(at: location)
      // Assume they moved off the node
      doingSomething = false
      for node in touchedNodes {
        if <some test for the interesting node> {
          // Nope, still touching it
          doingSomething = true
        }
      }
    }
  }
}

(You should also handle touchesCancelled in some appropriate way, maybe stopping the action and clearing activeTouch as in touchesEnded)

Perhaps you have some other behavior in mind for the case when there are multiple touches active. You might need to keep track of all the active touches and their status of being on or off the node, and then set doingSomething = true if any active touch is on the node. Or maybe you want touchesMoved to discard an active touch as soon as it moves off the node, so moving that touch back on again won't reactivate.

The main point is that keeping track of touch identity gives you a lot of flexibility, but you have to decide on how you want the game to react.