我在iOS 11(测试版)上使用ARKit和SceneKit编写iOS应用程序。

该应用程序将许多项目添加到增强现实场景中。为了识别每个项目,我将SCNNode子类化为MySCNNode,并在其中添加了id字段:

MySCNNode: SCNNode {
    var id: Int
    ...some initializer to assign value to the id
}

我还实现了touchesBegan委托方法。当用户触摸某个项目时,我想检索其对应的id。所以我尝试将该项目的对应SCNNode转换回MySCNNode:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)

    // hitTest to retrieve item's id
    if let touchLocation = touches.first?.location(in: sceneLocationView) {
        DDLogDebug("received touch location")
        if let hit = sceneLocationView.hitTest(touchLocation, options: nil).first {
            DDLogDebug("received a hit")
            if let scnNode = hit.node as? SCNNode {
                DDLogDebug("received a scnNode)")
                    if let mySCNNode = scnNode as? MySCNNode {
                        DDLogDebug("received a MySCNNode") // ====> Never reaches here
                    }

            }


            return
        }


    }


}

但是,从SCNNode转换为MySCNNode的步骤永远不会发生

在上述命中测试中尝试将SCNNode投射回MySCNNode时,是否写错了地方?是否有其他方法可以将其他信息存储在SCNNode中并在以后检索它?

谢谢

最佳答案

首先,您不需要id。相反,您可以使用现有的name属性。但是,如果要检查节点的类型,还应该检查它的父节点,因为hitTest结果节点从子节点开始。您可以添加SCNNode扩展:

extension SCNNode {
    /// Returns parent node of specified type.
    ///
    /// - Returns: An optional `SCNNode` of specified `T` class type.
    func parentOfType<T: SCNNode>() -> T? {
        var node: SCNNode! = self
        repeat {
            if let node = node as? T { return node }
            node = node?.parent
        } while node != nil
        return nil
    }
}

然后使用:
if let myNode = node.parentOfType() as? MySCNNode {
    // do something
}

09-07 14:40