Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect touch within Sprite texture and not the entire frame iOS Swift SpriteKit?

I am working with SpriteKit right now and I came across a problem that seems simple but I couldn't find anything on the internet. I have three buttons that are shaped like parallelograms stacked on top of each other and it looks like this: Screenshot of buttons

let button = SKSpriteNode(imageNamed: "playbutton")
let leaderButton = SKSpriteNode(imageNamed: "leaderbutton")
let homeButton = SKSpriteNode(imageNamed: "homebutton")

    button.position = CGPoint(x: size.width/2, y: size.height/2)
    addChild(button)

    leaderButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height/2)
    addChild(leaderButton)

    homeButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height)
    addChild(homeButton)

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch: AnyObject in touches {

        let location = touch.locationInNode(self)

        if button.containsPoint(location) {
            button.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        }
        else if leaderButton.containsPoint(location) {
            leaderButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        }
        else if homeButton.containsPoint(location) {
            homeButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        }
    }
}

This is how I am detecting touches. The problem is that they overlap because the sprite is actually a rectangle so when i try and tap on the top left of the second button, the top button detects it. I was wondering of there is a way to detect touch only in the texture like how you can set the physics body to a texture. Thanks for any help you can give me!

Link works now.

So I tried this:

    button.position = CGPoint(x: size.width/2, y: size.height/2)
    button.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "playbutton"), size: button.size)
    button.physicsBody?.dynamic = false
    addChild(button)

    leaderButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height/2)
    leaderButton.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "leaderbutton"), size: leaderButton.size)
    leaderButton.physicsBody?.dynamic = false
    addChild(leaderButton)

    homeButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height)
    homeButton.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "homebutton"), size: homeButton.size)
    homeButton.physicsBody?.dynamic = false
    addChild(homeButton)

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch: AnyObject in touches {

        let location = touch.locationInNode(self)

        if physicsWorld.bodyAtPoint(location)?.node == button {
            button.runAction(SKAction.scaleTo(0.8, duration: 0.1))
            print("play")
        }
        if physicsWorld.bodyAtPoint(location)?.node == leaderButton {
            leaderButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
            print("leader")
        }
        if physicsWorld.bodyAtPoint(location)?.node == homeButton {
            homeButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        }
    }
}

It still registers the full frame and not just the physics body. See the link to see the buttons and how their coordinates intersect.

like image 969
samando Avatar asked Oct 08 '15 05:10

samando


1 Answers

If you attach a physics body to each button, you can detect which physics body your touch lands on.

You can generate a physics body from the button's texture (assuming the button is an SKSpriteNode) using SKPhysicsBody(texture:size:) or SKPhysicsBody(texture:alphaThreshold:size:), or you can create a CGPath describing the button's shape and use SKPhysicsBody(polygonFromPath:). Assign the body to the button's physicsBody property. Assuming you don't actually want the physics simulator to move your buttons, set each body's dynamic property to false.

Then you can use physicsWorld.bodyAtPoint(_:) to get one of the bodies that a touch lands on (where physicsWorld is a property of your SKScene). Use the body's node property to get back to the button node. If bodies overlap, bodyAtPoint returns an arbitrary body.

You can use physicsWorld.enumerateBodiesAtPoint(_:usingBlock:) if you need all of the bodies that your touch lands on.

A completely different approach, if you can create a CGPath describing the button's shape, is to use SKScene.convertPointFromView(_:) and then SKNode.convertPoint(_:fromNode:_) to convert the point into the button's coordinate system, and then use CGPathContainsPoint (a global function) to detect whether the point is in the path describing the button shape.

like image 66
rob mayoff Avatar answered Oct 20 '22 19:10

rob mayoff