1
votes

I'm trying to make a game where the player at certain times draw a pattern on the screen. My solution for this is to add multiple nodes on the screen that are touchable via an extension of SKSpriteNode. When the player touches a node, I want to call touchesmoved, and add all nodes touched to an array. Then, when the player stops touching the screen, I want to match that array to another array, and something happens.

I've been playing around with the updates function and try to run a function each update loop, but it didn't work very well. I've also tried making the gameScene class a delegate of my touchableShapeNode Class, but I struggled to make it work.

class TouchableShapeNode: SKShapeNode {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if (name != nil) {
            print("\(name ?? "node") touched")

        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if (name != nil) { 
            print("\(name ?? "node") touched")

        }
    }
  }

My problem is that the only node that gets selected right now is the first node I touch, not the ones the player's finger move over. Right now I'm just printing the name of the touched node.

1
Hi Dan - I'm not sure from that code how even detects the first node touched, as there's nothing in your touchesBegan to establish the node at the touch. However, why not use touchesMoved` to simply examine the node at each touch location and aadd it to an array? Here's some code that flags a node as 'touched' in touchesBegan - I think all you'll need to so is to add similar code to touchesMoved stackoverflow.com/a/56490384/1430420Steve Ives
You can't do it this way. You need to do your touch at the scene level. A touchMoved event cant happen without a touchBegin, and the only way you are going to get a touchBegin is by having all of the nodes stacked on top of each other and ensuring none of them absorb the touchKnight0fDragon
@SteveIves Thanks for your reply! I'm not sure if I follow though :) Following your link, my problem is that when I call touchesMoved, nothing happens when I move my finger over any node except the first. This is what I tried: override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { if nodeIsTouched { for touch in touches { print("(nodes(at: touch.location(in: self)))") } } } But I still only get the first node I touch printed to consoleDaniel Ullenius
@DanielUllenius I've added an answer containing a small program that might help. Try it and if I've misunderstood your problem, let me know and I'll try and address it.Steve Ives

1 Answers

1
votes

I'm not exactly sure what you're after, but here's a little program that does the following:

  1. Puts 15 red blocks on screen
  2. As you drag around the screen, any nodes you touch are added to a set.
  3. When you stop touching, all touched nodes have their colour changed to green.
  4. When you start a new touch, the set of touched nodes is emptied and all nodes return to their starting colour (red).

To use, simply start a new, empty SpriteKit project and replace gameScene.swift with this code.

import SpriteKit
import UIKit

class GameScene: SKScene {

    let shipSize = CGSize(width: 25, height: 25)
    let normalColour = UIColor.red
    let touchedColour = UIColor.green
    var touchedNodes = Set<SKSpriteNode>()

    override func didMove(to view: SKView) {

        let sceneWidth = self.scene?.size.width
        let sceneHeight = self.scene?.size.height

        // Add 15 colour sprites to the screen in random places.
        for _ in 1...15 {
            let ship = SKSpriteNode(color: normalColour, size: shipSize)
            ship.position.x = CGFloat.random(in: -sceneWidth!/2...sceneWidth!/2) * 0.7
            ship.position.y = CGFloat.random(in: -sceneHeight!/2...sceneHeight!/2) * 0.7
            ship.name = "ship"
            addChild(ship)
        }
    }

    // When the screen is toucheed, empty the 'touchedNodes' set and rest all nodes back to their normal colour.
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        resetTouchedNodes()
    }

    // As the touch moves, if we touch a node then add it to our 'touchedNodes' set.
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first
        let location = touch!.location(in: self)

        // If there is a node at the touch location, add it to our 'touchedSprites' set.
        if let touchedNode = selectNodeForTouch(location) {
            touchedNodes.insert(touchedNode)
        }
    }

    // When the touch ends, make all nodes that were touched change colour.
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        for node in touchedNodes {
            node.color = touchedColour
        }
    }

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

    // Return the first sprite where the user touched the screen, else nil
    func selectNodeForTouch(_ touchLocation: CGPoint) -> SKSpriteNode? {
        let nodes = self.nodes(at: touchLocation)
        for node in nodes {
            if node is SKSpriteNode {
                return (node as! SKSpriteNode)
            }
        }
        return nil
    }

    // Clear the touchedSprites set and return all nodes on screen to their normal colour
    func resetTouchedNodes() {
        touchedNodes.removeAll()
        enumerateChildNodes(withName: "//ship") { node, _ in
            let shipNode = node as! SKSpriteNode
            shipNode.color = self.normalColour
        }
    }

}

You could amend this in various ways. For example, you could change the colour of the sprite immediately you touch it in touchesMoved etc.