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

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
    }
}

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


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

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

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

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


添加一个 UITapGestureRecognizer

1
let tapRec = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(rec:)))

state .ended 上的 handleTap 方法中:

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
        }
    }
}