0

I have an SKShapeNode that I have created and given a CGPath to. This is in my GameScene.swift in didMoveToView:

let myNode = SKShapeNode()
let lineToDraw = CGPathCreateMutable()
CGPathMoveToPoint(lineToDraw, nil, 0, 0)
CGPathAddLineToPoint(lineToDraw, nil, 87, 120)
myNode.path = lineToDraw
myNode.strokeColor = SKColor.redColor()
myNode.lineWidth = 20
myNode.name = "My Node!"
world.addChild(myNode) // World is a higher-level node in my scene

And this is how I'm detecting touches, again in the Scene:

override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
    if let touch = touches.anyObject() as? UITouch {
        if let shapeNode = nodeAtPoint(touch.locationInNode(self)) as? SKShapeNode {
            println("Touched \(shapeNode.name)")
        } else {
            println("Nothing here")
        }
    }
}

The line node is showing up with no issues. However, it is not registering touchesEnded at all.

Really, I have three questions nebulous to this:

  1. Is there a better way to create an SKShapeNode that is a line between two points without calling a convenience initializer? I plan on subclassing SKShapeNode to add additional logic to the node, and the subclass doesn't have access to convenience initializers of the superclass.

  2. More importantly, how do I get the scene to recognize my line node there and trigger touchesEnded?

  3. Is there a way, using that mechanism, I can make it a bit more "fuzzy", so it handles touches close to the line, instead of on the line? My eventual plan is to make it thinner, but I'd still like to have a large touch zone. I figure if nothing else, I can create two nodes: one clear/thick and the other red/thin, and handle touch events on the clear one, but I would like to know if there's a better way.

David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
Kate Bertelsen
  • 3,900
  • 5
  • 24
  • 18

1 Answers1

2

In answer to your main question, there is a problem in your touchesEnded method. The template shown by apple recommends the following layout:

override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)
    }
}

You can then use this method to see if the location value is inside the lines frame (in order to do this, i created the "myNode" variable outside of the didMoveToView so that i could access it from the rest of the functions):

override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            let location = touch.locationInNode(world)

            if CGRectContainsPoint(myNode.frame, location) {
                println("Touched \(myNode.name)")

            } else {

                println("Nothing here")
        }

    }
}

As for a "fuzzier" line, you can simply check a larger CGRect. In the code above, i check myNode.frame but you could create a variable with a CGRect which is slightly larger than the line so that you can detect touches which don't directly hit it.

As for more concise code, i cannot think of any at the moment but that isn't to say that there isn't a way. However, I don't quite understand what you mean about subclasses not having access to convenience methods as they can have access to whatever the superclass does so long as you import correctly.

I hope this helps.

Jack Chorley
  • 2,309
  • 26
  • 28
  • How would that scale up to having a dynamic number of lines on the screen to check against? Also, why do it that way instead of just grabbing `nodeAtPoint()`? https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Nodes/Nodes.html suggests `nodeAtPoint()` for determining what the top node that intersects a point is. – Kate Bertelsen Jul 01 '14 at 13:50
  • I would name each of the lines when they are initialised and then enumerate them to see how many were tapped. As for using `nodeAtPoint()`, i tried it and had the same problem as you where by the node was never detected. For this reason i used CGRectContainsPoint. – Jack Chorley Jul 01 '14 at 15:22
  • Going the `CGRectContainsPoint()` way, it detects a touch when nowhere near the line, because frames are rectangles, and my line is a diagonal line. So it thinks that I'm tapping "the line" because I'm tapping in the rectangle defined by the diagonal line. – Kate Bertelsen Jul 01 '14 at 23:17
  • 1
    @Reece55 I believe this method does work, but it may not detect touches very accurately. You may want to play around with it properly, especially on bigger shapes to test it to a better extent. It normally works well without any problems though sometimes the frame of the line is not a good representation of the actual line itself. Nonetheless, if it works for you then I glad I could help! – Jack Chorley Oct 22 '14 at 18:26