如何检测哪个SCNNode已被触摸ARKit
我在SCNode碰撞检测中遇到了一些麻烦。我需要检测具有SCNNode的场景中的哪个对象被触摸,我已经实现了这段代码,但是当我触摸对象时看起来崩溃,但是当我触摸sceneView的其余部分时工作正常。如何检测哪个SCNNode已被触摸ARKit
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")
}
}
}
这里有多种问题,而现有的答案是唯一的解决其中的一些。
这是一个从您发布
bottleNode
是否可以为空时,这种方法运行的代码不清楚。当值为零时通过可选项(?
中的)调用方法将自动失败 - 导致整个表达式结果包装为可选值,其值为nil - 但是您已经得到了parens和一个解压缩整个表达式的力,所以无解包装会崩溃。contains(_:)
不是SCNNode
上的方法。目前还不清楚你的bottleNode
是什么类型,你甚至可以编写这个方法调用而不会发生编译器错误......但是如果bottleNode
实际上是SCNNode
,并且你已经做了一些类型擦除/转换goop以允许调用编译由于不存在的方法,调用在运行时会失败。
如果与bottleNode.contains
线你的目标是确定命中测试结果是要么bottleNode
本身或其子节点,我建议定义和使用的扩展方法是这样的:
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")
}
如果您的目标是确定result.node
是否位于bottleNode
的边界框内(与节点层次结构无关),则回答该问题会稍微复杂一些。 boundingSphere
中的简单position
检查非常简单,或者如果您在寻找遏制/重叠,octree可能会有所帮助。
您目前正在测试,如果您SCNNode“载”,从则hitTest结果的节点,而你应该不是简单地对它们进行比较,也就是说,如果(bottlenode == result.node)...
有你实际上创建了一个bottleNode
?如果bottleNode
为零,则表达式bottleNode?.contains(result.node)
也将为零,因此当您强制解包时,它将引发错误。
如果你知道bottleNode总是被定义的,它不应该是可选的。如果你不知道,你需要在使用之前检查它是否存在。这应该做你想做的:
if let bottleNode = bottleNode, bottleNode.contains(result.node) {
请注意,如果bottleNode不存在,此测试将失败默默。
可能是,bottleNode
为零。什么生产线导致坠机?检查比较之前,如果存在bottleNode
:
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")
}
}
}
1。事实上在问题中没有提到,但是如果你真的想假设在针对该节点执行命中测试之前它可能为零,那么在进行命中测试之前实际执行该检查将是有意义的。 – Xartec
P2。除了无效的contains方法,没有理由假设bottleNode具有childnodes,但是如果是这种情况,是否有任何增加值使用自定义扩展来遍历结果节点的父母,而不是简单地检查bottlenode childnodes是否包含结果节点? (即瓶子节点上的enumerateHierarchy将避免检查结果节点的所有父母)。我确信在复制并粘贴代码后,结果会是由于扩展名为self == node部分,所以我不认为这应该是被接受的答案。 – Xartec
@Xartec好点。不知道所讨论的节点层次结构,我选择了一种通常在算法上高效的解决方案。向上移动父层次由节点树的高度限制,但是搜索目标节点的子子树受该子树的整个宽度限制。对于任意大树中的任何点,最坏情况下的子树大小可能远大于回到根的高度。但是值得考虑一下你期望在你的应用中使用的节点层次结构 - 如果它非常平坦,那么对测试节点进行简单的指针相等测试就足够了。 – rickster