2017-09-25 54 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") 
      } 

     } 
    }