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

Swift: can't detect hit test on SCNNode? See if vector3 is contained in a node?

我正在尝试检测在我的 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 中节点上的点击?

当您使用 ARSCNView 时,您可以进行两种命中测试,它们使用完全不同的代码路径。

  • 如果您想针对现实世界进行命中测试(或者至少,针对 ARKit 对真实世界特征位置的估计),请使用 ARKit 方法hitTest(_:types:) .这个 returns ARHitTestResult 对象,它告诉你真实世界的特征,比如检测到的平面。换句话说,如果您想找到一个任何人都可以在没有设备的情况下看到和触摸到的真实对象,请使用此方法 - 例如您将设备指向的 table。
  • 使用 SceneKit 方法 hitTest(_:options:) if you want to hit test against SceneKit content; that is, to search for virtual 3D objects you've placed in the AR scene. This returns SCNHitTestResult 对象,它告诉您节点和几何图形等信息。如果要查找 SceneKit 节点、节点中的模型(几何体)或点击位置几何体上的特定点,请使用此方法。

在这两种情况下,通过命中测试找到的 3D 位置是相同的,因为 ARSCNView 确保虚拟 "world coordinates" space 匹配真实世界 space .

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

添加一个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
        }
    }
}