我试图在我的AR场景中实例化的SCNNode上检测一个点击。它似乎没有像SceneKit那样在这里测试结果,我对场景套件没有多少经验。
我只想检测点击点是否包含在场景中的任何节点内,从而检测到对象的点击。我从其他答案中尝试过:
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语句打印,我得到了:
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
}
}
ARHitTestResult
不包含节点。如何检测ARScene中节点上的点击?
答案 0 :(得分:22)
当您使用ARSCNView
时,您可以执行两种命中测试,并且它们使用完全独立的代码路径。
hitTest(_:types:)
(或者至少针对ARKit对真实世界功能的估计)。这将返回ARHitTestResult
个对象,它们会告诉您有关检测到的平面等真实要素的信息。换句话说,如果您想要找到任何人都可以在没有设备的情况下看到和触摸的真实对象(例如您指向设备的表格),请使用此方法。 hitTest(_:options:)
;也就是说,搜索您放置在AR场景中的虚拟3D对象。这将返回SCNHitTestResult
个对象,它们会告诉您节点和几何等内容。如果要查找SceneKit节点,节点中的模型(几何体)或点击位置处几何体上的特定点,请使用此方法。在这两种情况下,命中测试找到的3D位置是相同的,因为ARSCNView
确保虚拟“世界坐标”空间与真实世界空间匹配。
看起来你正在使用前者,但期待后者。当您进行SceneKit命中测试时,当且仅当命中测试点下有节点时才会得到结果 - 您不需要任何类型的边界框测试,因为它已经为您完成了。
答案 1 :(得分:4)
添加UITapGestureRecognizer
let tapRec = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(rec:)))
在state .ended
上的handleTap方法中:
@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
}
}
}