2017-09-25 56 views
2

我在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") 
      } 

     } 
    } 

回答

3

这里有多种问题,而现有的答案是唯一的解决其中的一些。

  1. 这是一个从您发布bottleNode是否可以为空时,这种方法运行的代码不清楚。当值为零时通过可选项(?中的)调用方法将自动失败 - 导致整个表达式结果包装为可选值,其值为nil - 但是您已经得到了parens和一个解压缩整个表达式的力,所以无解包装会崩溃。

  2. 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可能会有所帮助。

+1

1。事实上在问题中没有提到,但是如果你真的想假设在针对该节点执行命中测试之前它可能为零,那么在进行命中测试之前实际执行该检查将是有意义的。 – Xartec

+0

P2。除了无效的contains方法,没有理由假设bottleNode具有childnodes,但是如果是这种情况,是否有任何增加值使用自定义扩展来遍历结果节点的父母,而不是简单地检查bottlenode childnodes是否包含结果节点? (即瓶子节点上的enumerateHierarchy将避免检查结果节点的所有父母)。我确信在复制并粘贴代码后,结果会是由于扩展名为self == node部分,所以我不认为这应该是被接受的答案。 – Xartec

+0

@Xartec好点。不知道所讨论的节点层次结构,我选择了一种通常在算法上高效的解决方案。向上移动父层次由节点树的高度限制,但是搜索目标节点的子子树受该子树的整个宽度限制。对于任意大树中的任何点,最坏情况下的子树大小可能远大于回到根的高度。但是值得考虑一下你期望在你的应用中使用的节点层次结构 - 如果它非常平坦,那么对测试节点进行简单的指针相等测试就足够了。 – rickster

1

您目前正在测试,如果您SCNNode“载”,从则hitTest结果的节点,而你应该不是简单地对它们进行比较,也就是说,如果(bottlenode == result.node)...

1

有你实际上创建了一个bottleNode?如果bottleNode为零,则表达式bottleNode?.contains(result.node)也将为零,因此当您强制解包时,它将引发错误。

如果你知道bottleNode总是被定义的,它不应该是可选的。如果你不知道,你需要在使用之前检查它是否存在。这应该做你想做的:

if let bottleNode = bottleNode, bottleNode.contains(result.node) { 

请注意,如果bottleNode不存在,此测试将失败默默。

+0

包含不是SCNNode的方法。 – Xartec

+0

@Xartec哎呀!好点子。 – Robert

2

可能是,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") 
      } 

     } 
    }