omerc
omerc

Reputation: 179

how to detect touch on node

I have an app thats spawn ball on the screen every 1 second. now, I want the user to touch those balls what make them disappear (removeFromParent()). as I understand I have to set the touch function via touchesBegan and I do so, here is my code:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch: AnyObject in touches{
        let positionOfTouch = touch.location(in: self)
        enumerateChildNodes(withName: "BALL") { (node: SKNode, nil) in
            if positionOfTouch == node.position {
                print("just touched the ball")
            }
            else{
                print("error")
            }
        }
    }

the problem is that when I touch the screen/ ball the console print error instead of just touched the ball, which mean that my code doesn't work. moreover, the console print the error message as the number of the balls in my view. i don't relay understand what I am doing wrong and how to really set this function. here is my createBall function which implement from my BallNode class (type SKShapeNode):

    func createBall(){
    let ball = BallNode(radius: 65)
    print(ball.Name)
    print(ball._subName!)

    ball.position.y = ((frame.size.height) - 200)
    let ballXPosition = arc4random_uniform(UInt32(frame.size.width)) // set the ball a randon position from the top of the screen
    ball.position.x = CGFloat(ballXPosition)

    ball.physicsBody?.categoryBitMask = PhysicsCategory.ball // ball's category bitMask
    ball.physicsBody?.collisionBitMask = PhysicsCategory.ball // prevent objects from intersecting
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.topBorder // when need to know if two objects touch each other

    addChild(ball)

}

can you help me with that? because I am quit new for swift I also would like to get some explanation about this touch detection (and touches in general - the apple doc is poor).

Upvotes: 1

Views: 2094

Answers (3)

Albertsen
Albertsen

Reputation: 165

Take a look at nodes(at:CGPoint) defined at SKNode to retrieve a list of the nodes at the touched position. You'll need to convert in between view coordinates and scene coordinates, though, using convertPoint(fromView). Documentation here and here.

Upvotes: 0

Ron Myschuk
Ron Myschuk

Reputation: 6061

every time you touch the screen you are cycling through all balls to see if you're touching one of them. if you have 50 balls on the screen it goes through them all to see if you are touching 1. that's not an efficient way of figuring out if you are touching 1.

There are many ways you can do this but what I would do is handle the touches inside of the Ball class. That way you don't have to figure out if you are touching a ball and which one it might be.

Explanation of protocol (to the best of my ability) this may seem a little much right now, but the faster you learn and understand protocols that better off you will be (IMO).

In this example we will use a protocol to setup a delegate of the BallNode class. A protocol is a set user defined "rules" that must be followed by any class that you designate compliant to that protocol. In my example I state that for a class to be compliant to the BallNodeDelegate protocol it must contain the didClick func. When you add the BallNodeDelegate after GameScene you are stating that this class will be compliant to that protocol. So if in GameScene you did not have the didClick func it will cause an error. All this is put in place so that you have an easy way to communicate between your BallNode instances and your GameScene class (without having to pass around references to your GameScene to each BallNode). Each BallNode then has a delegate (GameScene) which you can pass back the information to.

inside your BallNode class make sure you have isUserInteraction = true

outside of your BallNode class create a protocol that will send the touch info back to the GameScene

protocol BallNodeDelegate: class {
    func didClick(ball: BallNode)
}

create a delegate variable in your BallNode class

weak var delegate: BallNodeDelegate!

move the touches began to you BallNode class

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    self.delegate?.didClick(ball: self)
}

in GameScene add the compliance to the BallNode protocol

class GameScene: SKScene, BallNodeDelegate

in GameScene when you create a Ball make sure you set it's delegate

let ball = BallNode()
ball.delegate = self

in GameScene add the nest. func to handle the clicks

func didClick(ball: BallNode) {
    print("clicked ball")
}

Upvotes: 4

nathangitter
nathangitter

Reputation: 9777

You are comparing the exact touch point with the exact position of the node, which are very unlikely to ever be the same.

if positionOfTouch == node.position {

Instead, you'll need to test to see if the user's touch is close enough to the position of the ball.

One option is to use SKNode's contains function, which will handle this for you.

if node.contains(positionOfTouch) {

Side note: You'll probably want to use SKSpriteNode instead of SKShapeNode, as SKShapeNode has poor performance in SpriteKit.

Upvotes: 1

Related Questions