3

I'm working with Swift and SpriteKit.

I have the following situation :

enter image description here

Here, each of the "triangles" is a SKShapenode. My problem is that I would like to detect when someone touches the screen which triangle is being touched. I assume that the hitbox of all these triangles are rectangles so my function returns me all the hitboxes touched while I only want to know which one is actually touched.

Is there any way to have a hitbox that perfectly match the shape instead of a rectangle ?

Here's my current code :

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
    let touch = touches.first
    let touchPosition = touch!.locationInNode(self)
    let touchedNodes = self.nodesAtPoint(touchPosition)

    print(touchedNodes) //this should return only one "triangle" named node

    for touchedNode in touchedNodes
    {
        if let name = touchedNode.name
        {
            if name == "triangle"
            {
                let triangle = touchedNode as! SKShapeNode
                // stuff here
            }
        }
    }
}
Drakalex
  • 1,488
  • 3
  • 19
  • 39

3 Answers3

2

You could try to use CGPathContainsPoint with a SKShapeNode instead of nodesAtPoint, which is more appropriate:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
    let touch = touches.first
    let touchPosition = touch!.locationInNode(self)
    self.enumerateChildNodesWithName("triangle") { node, _ in
        // do something with node
        if node is SKShapeNode {
            if let p = (node as! SKShapeNode).path {
                if CGPathContainsPoint(p, nil, touchPosition, false) {
                    print("you have touched triangle: \(node.name)")
                    let triangle = node as! SKShapeNode
                    // stuff here
                }
            }
        }
    }
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • It is exactly what I wanted ! And all the nodes named "triangle" are SKShapeNodes, could we add after `enumerateChildNodesWithName` `let shapenode = node as! SKShapeNode`, and remove `if node is SKShapeNode` and `if let p = (node as! SKShapeNode).path` just to have ` if CGPathContainsPoint(shapenode.path, nil, touchPosition, false)` ? – Drakalex Jul 29 '16 at 12:29
  • It's not safetly, enumerateChildNodesWithName works with generic SKNode ;) , take a look to the apple guide: https://developer.apple.com/reference/spritekit/sknode/1483024-enumeratechildnodeswithname – Alessandro Ornano Jul 29 '16 at 12:32
2

This would be the easiest way of doing it.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
    for touch in touches {
        let location = touch.locationInNode(self)
        if theSpriteNode.containsPoint(location) {
             //Do Whatever    
        }
    }
}
Matthew Bergwall
  • 310
  • 1
  • 10
0

The way I do this with Swift 4:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else { 
     return 
    }
    let touchPosition = touch.location(in: self)
    let touchedNodes = nodes(at: touchPosition)
    for node in touchedNodes {
        if let mynode = node as? SKShapeNode, node.name == "triangle" {
            //stuff here
            mynode.fillColor = .orange //...
        }
    }

}
abanet
  • 1,327
  • 17
  • 22