Detecting touching a SKSpriteNode within a touchesBegan event?

Detecting touching a SKSpriteNode within a touchesBegan event?

My experience to date has focused on using GamepadControllers with Apps, not a touch-activated iOS App.

Here are some short code snippets:

Note: the error I am trying to correct is noted in the very first snippet = touchesBegan within the comment <== shows "horse"

Yes, there is a "horse", but it is no where near the "creditsInfo" SKSpriteNode within my .sksfile.

Please note that this "creditsInfo" SKSpriteNode is programmatically generated by my addCreditsButton(..) and will be placed very near the top-left of my GameScene.

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

    if let ourScene = GameScene(fileNamed: "GameScene") {
        
        if let touch:UITouch = touches.first {
            let location = touch.location(in: view)
            let node:SKNode = ourScene.atPoint(location)
            
            print("node.name = \(node.name!)")   // <== shows "horse"
            if (node.name == "creditsInfo") {
                showCredits()
            }
        }
        
    }   // if let ourScene
    
}   // touchesBegan

The above touchesBegan function is an extension GameViewController which according to the docs is okay, namely, touchesBegan is a UIView method besides being a UIViewController method.

Within my primary showScene() function, I have:

    if let ourScene = GameScene(fileNamed: "GameScene") {

#if os(iOS)
    addCreditsButton(toScene: ourScene)
#endif

     }

with:

func addCreditsButton(toScene: SKScene) {
            
    if thisSceneName == "GameScene" {
                    
        itsCreditsNode.name = "creditsInfo"

        itsCreditsNode.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        
        itsCreditsNode.size = CGSize(width: 2*creditsCircleRadius,
                                     height: 2*creditsCircleRadius)
        itsCreditsNode.zPosition = 3
        creditsCirclePosY = roomHeight/2 - creditsCircleRadius - creditsCircleOffsetY
        creditsCirclePosX = -roomWidth/2 + creditsCircleRadius + creditsCircleOffsetX
        itsCreditsNode.position = CGPoint(x: creditsCirclePosX,
                                          y: creditsCirclePosY)
        
        toScene.addChild(itsCreditsNode)
                    
    }   // if thisSceneName

}   // addCreditsButton

To finish, I repeat what I stated at the very top:

The error I am trying to correct is noted in the very first snippet = touchesBegan within the comment <== shows "horse"

Answered by JohnLove in 781627022

HERE is the THE answer ...

Make touchesBegan an extension of GameScene, not GameViewController with:

extension GameScene {
        
    // "if we set isUserInteractionEnabled = false, the Scene will receive touch events"
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        //  not needed because addCreditsButton() isn't called
        //  unless we're staring at "GameScene"
    //  if let ourScene = GameScene(fileNamed: "GameScene") {
            
            if let touch:UITouch = touches.first {
                let location = touch.location(in: self)
                let ourNode:SKNode = atPoint(location)   // not ourScene.atPoint(location)
                
            //  print("location = \(location)")
                print("ourNode.name = \(ourNode.name!)")
                if (ourNode.name == "creditsInfo") {
                    showCredits()
                }
            }   // if let touch:UITouch
            
    //  }   // if let ourScene
        
    }   // touchesBegan

I don't see anything obviously wrong in your code. The only possible problem I see is a coordinate system problem where the values of the touch location may not be what you expect because the view and the scene have different coordinates.

Set a breakpoint at the following line of code:

let node:SKNode = ourScene.atPoint(location)

Check if location is where you expect it to be.

I did check my .zPosition numbers and they were okay. I even set its .zPosition to a crazy 20 - no change. I also checked that node.isHidden = false and it is. So, it appears your statement that location is the culprit. As a matter of interest, physically this "info" SKSpriteNode is physically very close to the top-left corner .. so I clicked very close to this location and got, for example (1.0, 30.0). No matter how precise I tried to get (0.0, 0.0) I could never get that close vertically. So, that truly mystifies me. One last thing: .anchorPoint = (0.5, 0.5) AHAH = it's a circle with radius = 40.0, a left-edge offset from left scene border = 50.0 and a top-edge offset from top scene border = 15.0 That number = 30.0 mentioned above still bugs me greatly.

ALMOST THERE (1) change "let" to "var" + (2) add location.x = -roomWidth/2 + location.x & location.y = roomHeight/2 - location.y which together changes the view coordinates of the original location to match the node.location. I know I am on the right track because besides the "horse", I have several buildings and 2 people added directly to the .sks file. An appropriate print("\(node.name)") prints the names of the buildings + people. BUT, this does not work for my itsCreditsNode since this node is programmatically added, versus added to the .sks file. ??

Strictly speaking this is not the answer, but I present here what the last comment above talks about - but in a much more readable format. Hopefully this will generate some much needed help:

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

    if let ourScene = GameScene(fileNamed: "GameScene") {
        
        if let touch:UITouch = touches.first {
            var position = CGPoint()
            let location = touch.location(in: ourScene)
            position.x = -roomWidth/2 + location.x
            position.y = roomHeight/2 - location.y
            let node:SKNode = ourScene.atPoint(position)              
           
            if (node.name == "creditsInfo") {
                showCredits()
            }
        }            

    }   // if let ourScene
    
}   // touchesBegan

Please note that with the above touchesBegan, the node.name is not "creditsInfo", but "room" which is the SKSpriteNode immediately below my itsCreditsNode. "room" has zPosition = 0 versus 5 for itsCreditsNode. So, my itsCreditsNode is not seen.

Finally, note that my addCrreditsButton(...) presented at the top of this Post will not change

Accepted Answer

HERE is the THE answer ...

Make touchesBegan an extension of GameScene, not GameViewController with:

extension GameScene {
        
    // "if we set isUserInteractionEnabled = false, the Scene will receive touch events"
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        //  not needed because addCreditsButton() isn't called
        //  unless we're staring at "GameScene"
    //  if let ourScene = GameScene(fileNamed: "GameScene") {
            
            if let touch:UITouch = touches.first {
                let location = touch.location(in: self)
                let ourNode:SKNode = atPoint(location)   // not ourScene.atPoint(location)
                
            //  print("location = \(location)")
                print("ourNode.name = \(ourNode.name!)")
                if (ourNode.name == "creditsInfo") {
                    showCredits()
                }
            }   // if let touch:UITouch
            
    //  }   // if let ourScene
        
    }   // touchesBegan
Detecting touching a SKSpriteNode within a touchesBegan event?
 
 
Q