3

I'm creating a board game using Spritekit. Initially all the pieces (SKSpriteNode) are hidden. They are supposed to appear after I touch them? But since they are set hidden, I can't access them when I override the touchBegan function. What am I supposed to do?

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let location = touches.first!.locationInNode(self)
    let touchedNode = self.nodeAtPoint(location)
    if(touchedNode.name == "pieces"){
        if(touchedNode.hidden == true){
            touchedNode.hidden = false
        }
    }
}
user3499912
  • 81
  • 1
  • 1
  • 5

3 Answers3

2

What you are trying was working on iOS8. But that has been fixed in iOS9. If you try your code on iOS8 you will see that it works. This is one of the things that annoyed me on iOS8, because it doesn't make sense that you can tap on a hidden node. Still, it would be nice that you can tap a node with alpha 0.0 (works on iOS8 too), but this has been fixed as well in iOS9. So setting the alpha to 0.0 will not work.

The Solution

You can use SKNode's containsPoint method:

Returns a Boolean value that indicates whether a point lies inside the parent’s coordinate system.

Parent in your case would be your sprite. Read here how containsPoint method works. Basically, containsPoint doesn't care if the node is visible or not, or does it have a parent or not. It just checks if a certain point (location) lies inside of parent's (touchedNode) coordinate system. So here is how you can do it using this method:

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

        let location = touches.first!.locationInNode(self)

        self.enumerateChildNodesWithName("pieces") { node, stop in

            if node.containsPoint(location) && node.hidden == true {

                node.hidden = false
                stop.memory = true
            }
        }
    }

Here, you are enumerating through all of the pieces (I assumed that self is their parent), and if certain node contains touch location plus it is hidden, then make it visible (and stop the further, unnecessary search).

HINT:

Also consider using optional binding here:

if let touch = touches.first {

    let location = touch.locationInNode(self)

    //do your stuff here
}

It is good habit to use optional binding because it is safer.

Community
  • 1
  • 1
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • Thanks, it helped. – user3499912 Apr 12 '16 at 17:29
  • @user3499912 You are welcome. If this answer helped you to solve the issue, please mark it as accepted ;) – Whirlwind Apr 12 '16 at 20:27
  • Having spent hours debugging an app on iOS8 where a node was not firing `touchesBegan` because another hidden node in the same place was catching them first, I can tell you that it was a good fix that Apple did. – rghome Jun 05 '16 at 18:49
0

I have another approach that seems to be working so far:

//you can repeat the whitedisk for loop with black disk if you have to take turns. I am using the .name to identify which square was selected

func drawBoard() {

        let numRows = 8
        let numCols = 8
        let x = scene?.size.width
        xOffset = ((x)! - ((x)! / 9) * CGFloat(numCols))
        squareSize = CGSizeMake(x!/9, x!/9)
        diskSize = CGSizeMake(x!/11, x!/11)

        // Draw the squares
        for row in 0...numRows-1 {
            for col in 0...numCols-1 {
                square = SKSpriteNode(texture: SKTexture(imageNamed: "square.png"), size: squareSize!)
                //row and column both start with zero, may want to start with 1 later
                square!.name = "square \(col)-\(row)"
                square!.userInteractionEnabled = false
                square!.position = CGPointMake(CGFloat(col) * squareSize!.width + xOffset, CGFloat(row) * squareSize!.height + xOffset)
                self.addChild(square!)
            }
        }
        // Draw the White disks "hidden"
        for row in 0...numRows-1 {
            for col in 0...numCols-1 {
                let gamePiece = SKSpriteNode(texture: SKTexture(imageNamed: "whitedisk.png"), size: diskSize!)
                gamePiece.name = "whitedisk \(col)-\(row)"
                gamePiece.userInteractionEnabled = false
                //print("Disk Name: \(gamePiece.name)")
                gamePiece.position = CGPointMake(CGFloat(col) * squareSize!.width + xOffset, CGFloat(row) * squareSize!.height + xOffset)
                gamePiece.hidden = true
                self.addChild(gamePiece)
            }
        }
}

// disk variable is either whitedisk or blackdisk depending on whose turn it is this function displays the game piece after determining which square was selected

func placeDisk(name: String, disk: String) {
    var myName = name
    let theDisk = disk
    myName = myName.stringByReplacingOccurrencesOfString("square", withString: theDisk)
    let node = self.childNodeWithName(myName)
    //print("Display: \(myName) - Node: \(String(node))")
    node?.hidden = false
}

// I overode the touchesEND function but you could use touches began also

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch in touches {
            let positionInScene = touch.locationInNode(self)
            let name = self.nodeAtPoint(positionInScene).name
            if name != nil {
                print("Touched: \(name)")
               placeDisk(name!, disk: "whitedisk")
            }
        }
    }
John Carto
  • 165
  • 1
  • 4
0

This is from apple's documentation:

Handle User Interaction on Hidden Nodes by Using Alpha

Translucent nodes–those with an alpha of less than 1 but greater than 0–still receive user interactions. You can set a node's alpha to leastNonzeroMagnitude to make it effectively transparent and yet still respond to touches or mouse movements, although giving it a color of clear has the same effect.

In my board game i have a child object with clearcolor. It doesn't has a texture therefore it became invisible (if yours has a texture you can use leastNonzeroMagnitude i guess).

I have a .sks file and i make clear color like in this screenshot:

clear color

I select a custom color and set its opacity to 0.

I do detect it with this code:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let location = touch.location(in: self)
        let touchedNodes = nodes(at: location)
        let frontTouchedNode = atPoint(location).name
        print(touchedNodes)
        print(frontTouchedNode!)
    }

For more information a link to official Apple article: Controlling User Interaction on Nodes

EFE
  • 3,732
  • 4
  • 22
  • 30