Crashalot
Crashalot

Reputation: 34513

SpriteKit: How to detect touches on node that began outside of node

SKNode A is a parent of SKNode B.

If touches begin inside of SKNode B, the touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) function gets called.

If, however, the touches begin outside of SKNode B but move into SKNode B, the touchesMoved function never gets called.

One option is to handle all touch events inside SKNode A.

However, there are many nodes contained inside SKNode A. Ideally, we could push touch functionality into each child, as opposed to handling everything from SKNode A.

Is it possible to capture touch events that occur inside SKNode B even if they began outside of SKNode B?

Upvotes: 2

Views: 560

Answers (1)

Knight0fDragon
Knight0fDragon

Reputation: 16827

When you hit your touchesBegan method, your touch is limited to the coordinate system of that node until the point it is ended. Moving onto another node will not change the coordinate system to the new node. If B is the only node that requires the touch, then you can add a child to cover the entire scene to ensure that B gets fired.

class TouchNode : SKSpriteNode
{
    //if you need to override other inits do it here
    convenience init(withSize size:CGSize)
    {
       self.init(color:UIColor(red:0,green:0,blue:0,alpha:0.1),size: size)
       isUserInteractionEnabled = true

    }

    override func touchesBegan(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesBegan(touches:touches, withEvent:event)
    } 

    override func touchesMoved(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesMoved(touches:touches, withEvent:event)
    } 

    override func touchesEnded(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesEnded(touches:touches, withEvent:event)
    } 

    override func touchesCancelled(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesCancelled(touches:touches, withEvent:event)
    } 

}

Then in your NodeB class:

required init(coder aDecoder:NSCoder)
{
    super.init(coder:aDecoder)
    DispatchQueue.main.async{
        self.addChild(TouchNode(withSize:self.scene!.size))
    }
}
override func touchesBegan(touches: Set<UITouches>, withEvent event: UIEvent?) { 
    //do stuff
} 

override func touchesMoved(touches: Set<UITouches>, withEvent event: UIEvent?) { 
    //do stuff
} 

override func touchesEnded(touches: Set<UITouches>, withEvent event: UIEvent?) { 
    //do stuff
} 

override func touchesCancelled(touches: Set<UITouches>, withEvent event: UIEvent?) { 
    //do stuff
} 

Edit: Misread question, but leaving the answer in case somebody else needs to go from inside the node to outside.

Think about it, you want to touch nodeB outside of nodeB, this does not make sense. It is like me poking the air and you crying that I am poking you. But if you absolutely need to go outside of the bounds, then add a child node to the sprite when you touch it that extends beyond the node itself, and be sure to transfer the touch back up to the parent

class TouchNode : SKSpriteNode
{
    //if you need to override other inits do it here
    convenience init(withSize size:CGSize)
    {
       self.init(color:UIColor(red:0,green:0,blue:0,alpha:0.1),size: size)
       isUserInteractionEnabled = true

    }

    override func touchesBegan(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesBegan(touches:touches, withEvent:event)
    } 

    override func touchesMoved(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesMoved(touches:touches, withEvent:event)
    } 

    override func touchesEnded(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesEnded(touches:touches, withEvent:event)
    } 

    override func touchesCancelled(touches: Set<UITouches>, withEvent event: UIEvent?) { 
        parent!.touchesCancelled(touches:touches, withEvent:event)
    } 

}

Then in your NodeB class:

lazy var touchNode = TouchNode(withSize:self.scene!.size)

override func touchesBegan(touches: Set<UITouches>, withEvent event: UIEvent?) { 
    addChild(touchNode)
    //do other stuff
} 
override func touchesEnded(touches: Set<UITouches>, withEvent event: UIEvent?) { 
    touchNode.removeFromParent()
    //do other stuff
} 

override func touchesCancelled(touches: Set<UITouches>, withEvent event: UIEvent?) { 
    touchNode.removeFromParent()
    //do other stuff
} 

Upvotes: 2

Related Questions