Search code examples
swiftsprite-kituitouchtouchesbegan

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).


Solution

  • 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")
    }