I'm having some troubles with SCNode hit detection. I need to detect which object was touched in the scene having a SCNNode, I have implemented this piece of code but it seems crashing when I'm touching the object but working good when I'm touching the rest of the sceneView.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
if(touch.view == self.sceneView){
print("touch working")
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
if (bottleNode?.contains(result.node))! { //bottleNode is declared as SCNNode? and it's crashing here
print("match")
}
}
}
There are multiple problems here, and the existing answers are addressing only some of them.
It's unclear from the code you've posted whether bottleNode
can be nil when this method runs. Calling a method through the optional (the ?
in bottle?.contains
) when its value is nil would fail silently — causing the entire expression result to wrap in an Optional whose value is nil — but you've got parens and a force unwrap around the whole expression, so the nil-unwrap would crash.
contains(_:)
is not a method on SCNNode
. It's unclear what type your bottleNode
could be that you could even write this method call without getting compiler errors... but if bottleNode
actually is an SCNNode
and you've done some type-erasing/Any
-casting goop to allow the call to compile, the call would fail at runtime due to the non-existent method.
If your goal with the bottleNode.contains
line is to determine whether the hit test result is either bottleNode
itself or a child node thereof, I'd recommend defining and using an extension method like this:
extension SCNNode {
func hasAncestor(_ node: SCNNode) -> Bool {
if self === node {
return true // this is the node you're looking for
}
if self.parent == nil {
return false // target node can't be a parent/ancestor if we have no parent
}
if self.parent === node {
return true // target node is this node's direct parent
}
// otherwise recurse to check parent's parent and so on
return self.parent.hasAncestor(node)
}
}
// in your touchesBegan method...
if let bottleNode = bottleNode, result.node.hasAncestor(bottleNode) {
print("match")
}
If instead your goal is to determine whether result.node
lies within or overlaps the bounding box of bottleNode
(regardless of node hierarchy), answering that question is a little more complicated. A simple position
within boundingSphere
check is pretty easy, or if you're looking for containment/overlap, an octree might help.
It could be that, bottleNode
is nil. What line is causing the crash? Check if bottleNode
exists before the comparison:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
if(touch.view == self.sceneView){
print("touch working")
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
if let bottleNode = bottleNode, bottleNode == result.node { //bottleNode is declared as SCNNode? and it's crashing here
print("match")
}
}
}
You are currently testing if your SCNNode “contains” the node from the hittest result while you should instead simply compare them, i.e. if (bottlenode == result.node)...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With