1

I created a SCNView and want to use a UITapGestureRecognizer to move SCNNode's around the view; however, the code I implemented doesn't seem to be working. The nodes move sometimes, but they never move to the correct spot. Does anyone know what I am doing wrong here?

@objc func handleTapGesture(_ gesture: UITapGestureRecognizer) {
        let location = gesture.location(in: scnView)
        
        guard let result = self.scnView?.hitTest(location, options: nil).first else { return }
        let transform = result.simdModelTransform
        let newPosition = SCNVector3(transform.columns.3.x, node.position.y, transform.columns.3.z)
        
        node.position = newPosition
    }
helloworld12345
  • 176
  • 1
  • 4
  • 22

2 Answers2

1

You can also try using PanGesture as well. Let me give you sample code for the same.

let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(panGesture:)))
view.addGestureRecognizer(panRecognizer)


@objc func handlePan(panGesture: UIPanGestureRecognizer) {


 guard let view = view as? SCNView else { return }
let location = panGesture.location(in: self.view)
 switch panGesture.state {
case .began:
 guard let hitNodeResult = view.hitTest(location, options: nil).first else { return }
panStartZ = CGFloat(view.projectPoint(lastPanLocation!).z)
lastPanLocation = hitNodeResult.worldCoordinates
case .changed:
 let worldTouchPosition = view.unprojectPoint(SCNVector3(location.x, location.y, panStartZ!))
let movementVector = SCNVector3(
 worldTouchPosition.x - lastPanLocation!.x,
worldTouchPosition.y - lastPanLocation!.y,
worldTouchPosition.z - lastPanLocation!.z)
geometryNode.localTranslate(by: movementVector)
self.lastPanLocation = worldTouchPosition
default:
 break
}
}
Hetali Adhia
  • 506
  • 3
  • 13
  • This answer works if you add` lastPanLocation` and `panStartZ` as properties of the `UIViewController`; however, I would like the object not to move in the y-axis. Is there a good way to keep this consistent? For instance, I want to keep it on the same y-plane, while panning. – helloworld12345 Feb 28 '23 at 19:18
0

Looks like you are confused about usage of hitTest in SCNView. Firstly, note that hitTest(_:options:) will search for objects/nodes in the given scene corresponding to the ray-cast hits from point of touch. This explains why sometimes your code doesn't work because the hit test results will be empty in case the objects are not found.

To make things work as expected, you must project the point from CGPoint into scene using following code which also requires you to provide depth.

func pointToSCNVector3(view: SCNView, depth: Float, point: CGPoint) -> SCNVector3 {
    let projectedOrigin = view.projectPoint(SCNVector3Make(0, 0, depth))
    let locationWithz   = SCNVector3Make(Float(point.x), Float(point.y), projectedOrigin.z)
    return view.unprojectPoint(locationWithz)
}

then use the output to set the node position

@objc func handleTapGesture(_ gesture: UITapGestureRecognizer) {
        let location = gesture.location(in: scnView)
        node.position = pointToSCNVector3(view: scnView, depth: 50, point: location)
    }