6
votes

Good afternoon, I'm trying to figure out how to get touch notifications from an SCNNode & a SKSpriteNode from an SCNScene overlayed with a SKScene.

import UIKit
import SceneKit
class GameViewController: UIViewController {
var scnView:SCNView!
var scnScene:SCNScene!
var sprite: spritekitHUD!
var cameraNode: SCNNode!
var shape: SCNNode!


override func viewDidLoad() {
    super.viewDidLoad()
    setupScene()
     }

func setupScene() {
    scnView = self.view as! SCNView
    scnView.delegate = self
    scnView.allowsCameraControl = true
    scnScene = SCNScene(named: "art.scnassets/scene.scn")
    scnView.scene = scnScene
    sprite=spritekitHUD(size: self.view.bounds.size, game: self)
    scnView.overlaySKScene=sprite
    cameraNode = scnScene.rootNode.childNode(withName: "camera",
                                              recursively: true)!
    shape=scnScene.rootNode.childNode(withName: "shape", recursively: true)
    shape.name="ThreeDShape"
    }

 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
 let touch = touches.first!
 let location = touch.location(in: scnView)
 let hitResults = scnView.hitTest(location, options: nil)
 if let result = hitResults.first {
      handleTouchFor(node: result.node)
     }
  }

func handleTouchFor(node: SCNNode) {
  if node.name == "ThreeDShape" {
         print("SCNNode Touched")
    }
  }
}

This is my Spritekit overlay scene

import Foundation
import SpriteKit

class spritekitHUD: SKScene{

var game:GameViewController!
var shapeNode: SKSpriteNode!

init(size: CGSize, game: GameViewController){
    super.init(size: size)
    self.backgroundColor = UIColor.white

    let spriteSize = size.width/12
    self.shapeNode= SKSpriteNode(imageNamed: "shapeNode")
    self.shapeNode.size = CGSize(width: spriteSize, height: spriteSize)
    self.shapeNode.position = CGPoint(x: spriteSize + 8, y: spriteSize + 8)
    self.shapeNode.name="test"
    self.game=game


    self.addChild(self.pauseNode)

}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch=touches.first else{
        return
    }
    let location=touch.location(in: self)
    if self.atPoint(location).name=="test" {
     print("Spritekit node pressed")
    }
 }
}

so with this I can successfully get notifications that my spritenode has been touched on my overlaySKScene but I cant figure out how to get a notification that my SCNode has been touched. If you cant have 2 touchesbegan functions does anyone have any ideas how I can handle the 3d events with 2d events at the same time?

Thanks for your help!!

3

3 Answers

7
votes

If you want to use an SKScene overlay of an SCNView for user controls, (eg you want to implement a button in the SKScene overlay that "consumes" the touch), but also have touches that don't hit the controls to pass through and register in the underlying SCNView, you have to do this: set isUserInteractionEnabled to false on the SKScene overlay itself, but then to true on any individual elements within that overlay that you'd like to act as buttons.

let overlay = SKScene(fileNamed: "overlay")
overlay?.isUserInteractionEnabled = false
let pauseButton = overlay?.childNode(withName: "pauseButton") as? Button 
// Button is a subclass of SKSpriteNode that overrides touchesEnded
pauseButton?.isUserInteractionEnabled = true
sceneView.overlaySKScene = overlay

If the user touches a button, the button's touch events (touchesBegan, touchesEnded etc) will fire and consume the touch (underlying gesture recognizers will still fire though). If they touch outside of a button however, the touch will pass through to the underlying SCNView.

4
votes

This is "lifted" straight out of Xcode's Game template......

Add a gesture recognizer in your viewDidLoad:

       // add a tap gesture recognizer
       let tapGesture = UITapGestureRecognizer(target: self, action:  
           #selector(handleTap(_:)))
        scnView.addGestureRecognizer(tapGesture)

    func handleTap(_ gestureRecognize: UIGestureRecognizer) {
    // retrieve the SCNView
    let scnView = self.view as! SCNView

    // check what nodes are tapped
    let p = gestureRecognize.location(in: scnView)
    let hitResults = scnView.hitTest(p, options: [:])
    // check that we clicked on at least one object
    if hitResults.count > 0 {
        // retrieved the first clicked object
        let result: AnyObject = hitResults[0]

        // result.node is the node that the user tapped on
        // perform any actions you want on it


    }
}
0
votes

You can implement this method in spritekitHUD:

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