I have implemented multi-touch dragging of SKSpriteNodes on my game.
It is working, but of course, there is a bug.
In the iOS simulator, Case 1: if I drag a node around the viewable screen, then continue to drag the node off the viewable screen, it will disappear and then continue to drag the node back onto the screen it will appear properly. This is good. It only works if I do it slowly.
Case 2: If I do the exact same movement (as mentioned in Case 1) except this time I do it very quickly, the node, when it reappears on the screen will "stick" to a spot on the screen. I have not yet ended the touch at this point, i.e. the mouse button is still being held down. On the simulator it looks like I am dragging nothing around.
The intended behaviour is once the touch starts, create the node at the touched location (working good), drag the node around (working) and then if the node is dragged off the screen, the touch should end, and the node should be deleted.
The nodes and touches are being tracked in a dictionary array.
In the screen shot, I clicked once, the node appeared, held down the entire time and started dragging it off and onto the left side of the screen quickly many times. It created many nodes that should have disappeared. If I drag slowly off the screen then back onto the screen, it won't make "extra" nodes.
I have placed a simplified version of my code on GitHub for you to try out the issue for yourself. I have been spending about 10 hours on this problem and have found no solution. Your help is greatly appreciated. GitHub repository StackOverflow Game1 project
var selectedNodes:[UITouch:SKSpriteNode] = [UITouch:SKSpriteNode]()
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch:AnyObject in touches{
var location = touch.locationInNode(self)
// Add in the node
var hero = SKSpriteNode(imageNamed: "Player")
hero.name = "player"
// Make sure it is not off of the screen.
hero.position = location
self.addChild(hero)
let touchObj = touch as UITouch
selectedNodes[touchObj] = hero
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch:AnyObject in touches {
//println("touhex count \(touches.count)")
let location = touch.locationInNode(self)
let touchObj = touch as UITouch
//println("Touches moved current loc: \(location)")
// Update the position of the sprites
if let node:SKSpriteNode? = selectedNodes[touchObj] {
if (node?.name == "player" || node?.name == "bar")
{
let size:CGFloat = CGFloat(node!.size.width)/2
node?.position = location
}
}
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch:AnyObject in touches {
let touchObj = touch as UITouch
let location = touch.locationInNode(self)
println("Touch Ended \(location)")
if let exists:AnyObject? = selectedNodes[touchObj] {
println("attempt to remove")
let node:SKSpriteNode? = self.nodeAtPoint(location) as? SKSpriteNode
println("ended touch locaiton \(location)")
println("ended npde position \(node?.position)")
for (node) in selectedNodes{
println("Touch n1 \(node.1.position)")
}
if (node?.name == "player")
{
println("removed")
node?.removeFromParent()
selectedNodes[touchObj] = nil
}
}
}
}
The problem is that when you move it fast, the touchesEnded
function may fire without the position being updated.
In your touchesEnded
function, instead of using nodeAtPoint
just use exists.removeFromParent()
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch:AnyObject in touches {
let touchObj = touch as UITouch
if let exists:SKNode = selectedNodes[touchObj] {
if (exists.name == "player")
{
exists.removeFromParent()
selectedNodes[touchObj] = nil
}
}
}
}