Swift:无法在SCNNode上检测到命中测试?看看vector3是否包含在一个节点中?

sky*_*guy 2 ios scenekit swift swift4 arkit

我试图在我的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))
        {

        }

    }
Run Code Online (Sandbox Code Playgroud)

没有用最后一个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
    }
}
Run Code Online (Sandbox Code Playgroud)

并且ARHitTestResult不包含节点.如何检测ARScene中节点上的点击?

ric*_*ter 24

当您使用时ARSCNView,您可以执行两种命中测试,并且它们使用完全独立的代码路径.

  • hitTest(_:types:)如果你想针对现实世界进行测试(或者至少是针对ARKit对现实世界特征的估计),请使用ARKit方法.这将返回ARHitTestResult对象,这些对象会告诉您有关检测到的平面等真实要素的信息.换句话说,如果您想要找到任何人都可以在没有设备的情况下看到和触摸的真实对象(例如您指向设备的表格),请使用此方法.
  • hitTest(_:options:)如果要对SceneKit内容进行测试,请使用SceneKit方法; 也就是说,搜索您放置在AR场景中的虚拟3D对象.这会返回SCNHitTestResult对象,告诉您有关节点和几何的信息.如果要查找SceneKit节点,节点中的模型(几何体)或点击位置处几何体上的特定点,请使用此方法.

在这两种情况下,命中测试找到的3D位置是相同的,因为ARSCNView确保虚拟"世界坐标"空间与真实世界空间匹配.

看起来你正在使用前者,但期待后者.当您进行SceneKit命中测试时,当且仅当命中测试点下有节点时才会得到结果 - 您不需要任何类型的边界框测试,因为它已经为您完成了.

  • 您不会将 SceneKit 节点放置在“实际世界”中。至少,并非没有相当多的额外软件和 3D 打印机。但是,使用`ARSCNView`,SceneKit 节点的位置和大小是在“实际世界”测量中,因此您仍然可以使用 SceneKit 方法来找到它们。尝试从一个向量(通过 `ARHitTestResult` 获得)返回到一个节点只是编写(以及调试和维护)比你需要的更多的代码。编辑答案以澄清。 (2认同)

小智 6

添加一个 UITapGestureRecognizer

let tapRec = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(rec:)))
Run Code Online (Sandbox Code Playgroud)

在您的handleTap方法中state .ended

@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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)