Search code examples
swiftmatrixsprite-kittouchskspritenode

How do I detect which SKSpriteNode has been touched


I find a similar question, but I am trying to detect and identify which Sprite the user touch, and I don't know how to do that. This is my variable:

var sprites: [[SKSpriteNode]] = [[SKSpriteNode(imageNamed: "a"), SKSpriteNode(imageNamed: "b")], [SKSpriteNode(imageNamed: "c"),SKSpriteNode(imageNamed: "d")]]

The idea is identify the spriteNode and then replace it for other sprite or change the color, but I don´t know how to do it with this matrix of spriteNodes, I guess the first step it´s identify the sprite.


Solution

  • What you are trying to do (even if I don't see a reason for this) can be accomplished using delegation pattern. Basically, you will tell your delegate (the scene, or whatever you set as a delegate) to do something for you, and you will do that directly from within the button's touchesBegan method. Also, you will pass the button's name to a scene.

    To make this happen, first you have to define a protocol called ButtonDelegate. That protocol defines a requirement which states that any conforming class has to implement a method called printButtonsName(_:):

    protocol ButtonDelegate:class {
    
       func printButtonsName(name:String?)
    }
    

    This is the method which will be implemented in your GameSceneclass, but called from within button's touchesBegan. Also, this method will be used to pass a button's name to its delegate (scene), so you will always know which button is tapped.

    Next thing is button class itself. Button might look like this:

    class Button : SKSpriteNode{
    
        weak var delegate:ButtonDelegate?
    
        init(name:String){
            super.init(texture: nil, color: .purpleColor(), size: CGSize(width: 50, height: 50))
            self.name = name
            self.userInteractionEnabled = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    
            delegate?.printButtonsName(self.name)
        }  
    }
    

    The important thing here is userInteractionEnabled = true, which means that button will accept touches. Another important thing is a delegate property. As already mentioned, buttons will have the scene set as their delegate. Setting a scene as delegate of buttons will be done later when we create some buttons... To make this easier for you, think of a delegate as a worker who works for his boss :) The boss (a button) tells his worker (a scene) to do something for him (to prints his name).

    Okay, so lets make sure that scene conforms to a ButtonDelegate protocol...Why is this important? It is important because the worker (scene) must follow the orders of his boss (a button). By conforming to this protocol, the worker is making a contract with his boss where confirming that he knows how to do his job and will follow his orders :)

    class GameScene: SKScene, ButtonDelegate {
    
    
        override func didMoveToView(view: SKView) {
    
            let play = Button(name:"play")
            play.delegate = self
            let stop = Button(name:"stop")
            stop.delegate = self
    
            play.position = CGPoint(x: frame.midX - 50.0, y: frame.midY)
            stop.position = CGPoint(x: frame.midX + 50.0, y: frame.midY)
    
            addChild(play)
            addChild(stop)
        }
    
        func printButtonsName(name: String?) {
    
            if let buttonName = name {
                print("Pressed button : \(buttonName) ")
            }
    
            //Use switch here to take appropriate actions based on button's name (if you like)
        }
    }
    

    And that's it. When you tap the play button, the touchesBegan on a button itself will be called, then the button will tell its delegate to print its name using the method defined inside of scene class.