如何检测哪个 SCNNode 被触摸了 ARKit

How to detect which SCNNode has been touched ARKit

我在 SCNode 命中检测方面遇到了一些问题。我需要检测在具有 SCNNode 的场景中触摸了哪个对象,我已经实现了这段代码,但是当我触摸对象时它似乎崩溃了但是当我触摸场景视图的其余部分时它工作正常。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first as! UITouch
        if(touch.view == self.sceneView){
            print("touch working")
            let viewTouchLocation:CGPoint = touch.location(in: sceneView)
            guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
                return
            }
            if (bottleNode?.contains(result.node))! { //bottleNode is declared as  SCNNode? and it's crashing here
                print("match")
            }

        }
    }

您当前正在测试您的 SCNNode 是否“包含”来自最命中结果的节点,而您应该简单地比较它们,即 if (bottlenode == result.node)...

你真的创建了一个bottleNode吗?如果 bottleNode 为 nil,表达式 bottleNode?.contains(result.node) 也将为 nil,因此当您强制展开它时,它会抛出错误。

如果您知道 bottleNode 将始终被定义,那么它不应该是可选的。如果您不知道,则需要在使用前检查它是否存在。这应该可以满足您的要求:

if let bottleNode = bottleNode, bottleNode.contains(result.node) {

请注意,如果 bottleNode 不存在,此测试将自动失败。

可能是,bottleNode为零。什么线路导致崩溃?比较前检查bottleNode是否存在:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first as! UITouch
        if(touch.view == self.sceneView){
            print("touch working")
            let viewTouchLocation:CGPoint = touch.location(in: sceneView)
            guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
                return
            }
            if let bottleNode = bottleNode, bottleNode == result.node { //bottleNode is declared as  SCNNode? and it's crashing here
                print("match")
            }

        }
    }

这里有多个问题,现有的答案只解决了其中的一部分。

  1. 从您发布的代码中不清楚当此方法运行时 bottleNode 是否可以为 nil。通过可选值(bottle?.contains 中的 ?)调用一个方法,当它的值为 nil 时会无提示地失败——导致整个表达式结果包装在一个值为 nil 的 Optional 中——但是你有括号和一个力展开整个表达式,所以 nil-unwrap 会崩溃。

  2. contains(_:) 不是 SCNNode 上的方法。目前还不清楚你的 bottleNode 可能是什么类型,你甚至可以在不出现编译器错误的情况下编写这个方法调用......但是如果 bottleNode 实际上是一个 SCNNode 并且你已经做了一些类型 -擦除/Any-casting goop 以允许编译调用,由于方法不存在,调用将在运行时失败。

如果您使用 bottleNode.contains 行的目标是确定命中测试结果是 bottleNode 本身还是其子节点,我建议定义和使用这样的扩展方法:

extension SCNNode {
    func hasAncestor(_ node: SCNNode) -> Bool {
        if self === node {
            return true // this is the node you're looking for
        }
        if self.parent == nil {
            return false // target node can't be a parent/ancestor if we have no parent
        } 
        if self.parent === node {
            return true // target node is this node's direct parent
        }
        // otherwise recurse to check parent's parent and so on
        return self.parent.hasAncestor(node)
    }
}

// in your touchesBegan method...
if let bottleNode = bottleNode, result.node.hasAncestor(bottleNode) { 
    print("match")
}

如果您的目标是确定 result.node 是否位于 bottleNode 的边界框内或与其重叠(不考虑节点层次结构),则回答该问题会稍微复杂一些。 boundingSphere 中的简单 position 检查非常简单,或者如果您要查找 containment/overlap,octree 可能会有所帮助。