6

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.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
AnaMM
  • 307
  • 4
  • 17
  • Hello! 0.0 @rakeshbs – AnaMM Apr 07 '16 at 02:00
  • For start, don't create sprites like that. You have to set their names. Later, in touchesBegan , you can use `nodeAtPoint` or `nodesAtPoint` to check which node is *touched* and based on their name take appropriate actions. If you need some code let know... – Whirlwind Apr 07 '16 at 05:43
  • Actually, the answer posted by rakeshbs in that link is how you should do it. So you have to create buttons, name them, add them to the matrix (if you like) and use the code he posted. – Whirlwind Apr 07 '16 at 05:49
  • Yes that is what I am trying. Do you know the way to get the name of the sprite the user touch using the code of rakeshbs? I mean, send the name to my scene from the class touchesprite for change the color of the sprite, because I can't do it. Thanks you! @Whirlwind – AnaMM Apr 09 '16 at 22:32
  • It is all explained there. He has shown two ways : 1) Touch detection is handled inside of a game scene. 2) Touch detection is handled inside of touchesBegan directly in a subclass of an SKNode. Which one of these two examples you are trying to implement ? – Whirlwind Apr 09 '16 at 22:37
  • The second, thats why I am not very sure how to get the name of the sprite! How to get that name in the scene for used :( @Whirlwind – AnaMM Apr 09 '16 at 22:41
  • I see.. Okay, will make my answer shortly. – Whirlwind Apr 09 '16 at 22:43
  • Still, I think you are using wrong method here, and complicate things without a real need. Those buttons...What is the reason of doing things inside of button's touches began, rather then inside of scene's touches began? – Whirlwind Apr 09 '16 at 22:59
  • Your right! It is better to put it inside the scene´s touches began and it is not so complicated. I will try! Thank you very much for you help and your patience! :) @Whirlwind – AnaMM Apr 09 '16 at 23:08
  • Yeah, you should probably stick to the easier techniques if there is no real reason for some more complicated stuff. Anyways, I've made an example for you if you are curious how it can be done. Just copy and paste code and see how it works (I assumed that your scene and a view have the same size). – Whirlwind Apr 09 '16 at 23:53

2 Answers2

13

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.

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • WOW!!! Thank you! You have no idea how much I really appreciate your help! You are amazing! Thanks! :D – AnaMM Apr 10 '16 at 00:05
  • why is there a need to call a function in the delegate? Why can't the button do the required activity itself, the moment it realised it's touched? – Confused Sep 27 '16 at 20:09
  • @confused Well you can, but it really depends on what should happen when button is pressed. You may want to add / remove a node to a scene or make a transition. Or remove node from a scene, so in these situations, delegate would be useful. You can use blocks though, and pass a block of code to a button instance, but I like more this way of delegating responsibility. You haven't really defined what do you exactly mean by *how* button should "do activity itself" , so I can't really comment more than this. – Whirlwind Sep 28 '16 at 05:29
  • "do activity itself" means have the touchesBegan function override inside the button's code. – Confused Sep 28 '16 at 12:16
  • which is to say I'm very confused about code architecture choices. – Confused Sep 28 '16 at 12:29
  • 1
    I agree with delegation because in the question it states "The idea is identify the spriteNode and then replace it for other sprite or change the color," If you want to change the sprite the delegate would make that call not the sprite touched. He didn't say change texture. – Skyler Lauren Sep 28 '16 at 15:33
  • I have button from SKShapeNode and SKLabelNode. When I touch the button, I need to change the text on the SKLabelNode. How to implement this using the delegate pattern? – Eugene Oct 14 '20 at 19:58
1

First, you need another way to create the sprite, here are a way:

let spriteA = SKSpriteNode(imageNamed: "a")
scene.addChild(spriteA)
let spriteB = SKSPriteNode(imageNamed: "b")
scene.addChild(spriteB)
...and so on...

Now we needs to set a name for the sprite so we can know which node is tapped later. To add a name for a sprite just do this:

spriteNode.name = "name of the sprite"

Putting this code in the above example will look something like this:

let spriteA = SKSpriteNode(imageNamed: "a")
spriteA.name = "a"
scene.addChild(spriteA)
let spriteB = SKSPriteNode(imageNamed: "b")
spriteB.name = "b"
scene.addChild(spriteB)
...and so on...

To detect touches put this into your SKScene subclass:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first as UITouch!
    let touchLocation = touch.locationInNode(self)
    let targetNode = nodeAtPoint(touchLocation) as! SKSpriteNode
}

The targetNode is the node you tapped.

If you wants to get the name of the sprite you can use targetNode.name.

Tom Shen
  • 1,838
  • 3
  • 19
  • 40