Swift: can't detect hit test on SCNNode? See if vector3 is contained in a node?
我试图检测在我的 AR 场景中实例化的 SCNNode 上的点击。它似乎不像 SceneKit 在这里命中测试结果那样工作,而且我对场景套件没有太多经验。
我只想检测点击点是否包含在场景中的任何节点中,从而检测对象上的点击。我从其他答案中尝试过的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let results = sceneView.hitTest(touch.location(in: sceneView), types: [ARHitTestResult.ResultType.featurePoint]) guard let hitFeature = results.last else { return } let hitTransform = SCNMatrix4.init(hitFeature.worldTransform) let hitPosition = SCNVector3Make(hitTransform.m41, hitTransform.m42, hitTransform.m43) if(theDude.node.boundingBoxContains(point: hitPosition)) { } } |
我得到的最后一个 if 语句没有打印任何内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | extension SCNNode { func boundingBoxContains(point: SCNVector3, in node: SCNNode) -> Bool { let localPoint = self.convertPosition(point, from: node) return boundingBoxContains(point: localPoint) } func boundingBoxContains(point: SCNVector3) -> Bool { return BoundingBox(self.boundingBox).contains(point) } } struct BoundingBox { let min: SCNVector3 let max: SCNVector3 init(_ boundTuple: (min: SCNVector3, max: SCNVector3)) { min = boundTuple.min max = boundTuple.max } func contains(_ point: SCNVector3) -> Bool { let contains = min.x <= point.x && min.y <= point.y && min.z <= point.z && max.x > point.x && max.y > point.y && max.z > point.z return contains } } |
并且
当您使用
-
如果您想针对真实世界(或至少针对 ARKit 对真实世界特征所在位置的估计)进行测试,请使用 ARKit 方法
hitTest(_:types:) 。这会返回ARHitTestResult 对象,这些对象会告诉您真实世界的特征,例如检测到的平面。换句话说,如果您想找到一个任何人都可以在没有设备的情况下看到和触摸的真实对象,例如您将设备指向的桌子,请使用此方法。 -
如果要对 SceneKit 内容进行测试,请使用 SceneKit 方法
hitTest(_:options:) ;也就是说,搜索您放置在 AR 场景中的虚拟 3D 对象。这会返回SCNHitTestResult 对象,这些对象会告诉您诸如节点和几何图形之类的信息。如果要查找 SceneKit 节点、节点中的模型(几何体)或几何体上点击位置的特定点,请使用此方法。
在这两种情况下,命中测试找到的 3D 位置是相同的,因为
看起来您正在使用前者,但期待后者。当您进行 SceneKit 命中测试时,当且仅当命中测试点下方有一个节点时,您才会得到结果,因为您不需要任何类型的边界框测试,因为它已经为您完成了。
添加一个
1 | let tapRec = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(rec:))) |
在
1 2 3 4 5 6 7 8 9 | @objc func handleTap(rec: UITapGestureRecognizer){ if rec.state == .ended { let location: CGPoint = rec.location(in: sceneView) let hits = self.sceneView.hitTest(location, options: nil) if let tappednode = hits.first?.node { //do something with tapped object } } } |