如何使用ARKit检测触摸并显示新的SCNPlane?

时间:2018-08-30 06:32:15

标签: swift scenekit arkit scnnode

现在,当检测到卡时,我可以显示不同的SCNPlane。显示SCNPlanes后,用户触摸任何平面以显示新的SCNPlane。但是目前触摸正常,但是新的SCNPlane没有显示。

这是我尝试过的代码:

var cake_1_PlaneNode : SCNNode? = nil

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    guard let imageAnchor = anchor as? ARImageAnchor else { return }

    if let imageName  = imageAnchor.referenceImage.name {            
        print(imageName)

        if imageName == "menu" {

            // Check To See The Detected Size Of Our menu Card (Should By 5cm*3cm)
            let menuCardWidth = imageAnchor.referenceImage.physicalSize.width
            let menuCardHeight =  imageAnchor.referenceImage.physicalSize.height

            print(
                """
                We Have Detected menu Card With Name \(imageName)
                \(imageName)'s Width Is \(menuCardWidth)
                \(imageName)'s Height Is \(menuCardHeight)
                """)

            //raspberry
            //cake 1

            let cake_1_Plane = SCNPlane(width: 0.045, height: 0.045)
            cake_1_Plane.firstMaterial?.diffuse.contents = UIImage(named: "france")
            cake_1_Plane.cornerRadius = 0.01

            let cake_1_PlaneNode = SCNNode(geometry: cake_1_Plane)
            self.cake_1_PlaneNode = cake_1_PlaneNode
            cake_1_PlaneNode.eulerAngles.x = -.pi/2
            cake_1_PlaneNode.runAction(SCNAction.moveBy(x: 0.15, y: 0, z: -0.125, duration: 0.75))

            node.addChildNode(cake_1_PlaneNode)
            self.sceneView.scene.rootNode.addChildNode(node)                
        }  
   }       

   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 planeNode = cake_1_PlaneNode, cake_1_PlaneNode == result.node{
            print("match")
            cake_1() 
        }
    }
}

func cake_1() {

    let plane = SCNPlane(width: 0.15  , height: 0.15)
    plane.firstMaterial?.diffuse.contents = UIColor.black.withAlphaComponent(0.75)

    let planeNodee = SCNNode(geometry: plane)
    planeNodee.eulerAngles.x = -.pi / 2
    planeNodee.runAction(SCNAction.moveBy(x: 0.21, y: 0, z: 0, duration: 0))

} //cake_1

关注此链接:Detect touch on SCNNode in ARKit

2 个答案:

答案 0 :(得分:6)

查看您的代码,我会看到几个问题(更不用说变量和方法的命名约定了。)

首先,您要创建一个Global Variable,并声明如下:

var cake_1_PlaneNode : SCNNode? = nil

不过,Local中的Global Variable既使用cake_1_PlaneNode也使用Delegate Callback

let cake_1_PlaneNode = SCNNode(geometry: cake_1_Plane)
self.cake_1_PlaneNode = cake_1_PlaneNode

应该这样简单地读:

self.cake_1_PlaneNode = SCNNode(geometry: cake_1_Plane)

第二,您将cake_1_PlaneNode添加到rootNode的{​​{1}}中,而不是检测到的ARSCNView,这可能是您不想执行的操作,因为当检测到ARImageAnchor时:

  

您可以通过附加几何图形来为锚提供视觉内容   (或其他SceneKit功能)添加到该节点或通过添加子节点。

因此,此方法(除非您实际上想这样做)是不必要的。

其余问题位于ARAnchor函数本身内。

首先,您实际上并没有将cake_1添加到planeNodee中。

由于您尚未指定是否应将新初始化的sceneHierachy直接添加到您的planeNode或作为ARSCNView中{{1} }}应该包含以下其中一项:

childNode

此外,由于默认情况下cake_1_planeNode是垂直呈现的,因此可能也不需要旋转function

由于您未指定要放置内容的位置,因此可能不需要使用self.sceneView.scene.rootNode.addChildNode(planeNodee) self.cake_1_planeNode.addChildNode(planeNodee) ,因为这实际上会使肉眼看不见。

Z位置是另一个可能导致您在实际添加节点时看不到节点的问题。

如果将2个节点设置在同一位置,则可能会遇到称为planeNodee的问题(您可以了解有关here的更多信息)。因此,您可能应该在添加节点时稍微向前移动添加的节点,例如SCNPlane来解决这个问题。

基于所有这些观点,我在下面提供了一个完整的工作示例,并带有注释:

-.pi / 2

在我的设备上会产生以下内容:

enter image description here

为您提供信息,这似乎表明您放置内容的计算已关闭(除非这是期望的结果)。

如您所见,所有内容均正确呈现,但是这些内容的间距很大,因此,在进行进一步测试和开发时,您可能需要稍微平移设备才能看到所有内容。

希望有帮助...

答案 1 :(得分:3)

***请为变量和函数使用描述性的明确名称,这很难阅读和理解您的代码。您可以在此处阅读有关快速样式指南的更多信息:https://github.com/raywenderlich/swift-style-guide#naming

当用户触摸屏幕时,您正在创建一个新平面,但是您没有将该平面添加到场景中,因此您的“ cake_1()”函数仅创建一个新平面。 当ARKit检测到图像时,它会自动创建一个空节点并将其添加到我们检测到的图像中心的场景中。当检测到图像时,我们必须首先保留对ARKit为我们添加的节点的引用。 将此变量添加到类的顶部:

var detectedImageNode: SCNNode?

然后在func渲染器中(渲染器:didAdd节点:,用于锚点:)添加以下行:

detectedImageNode = node

现在,我们有了对节点的引用,我们可以轻松地添加和删除其他节点。 在cake_1()的末尾添加以下行:

if let detectedImageNode = detectedImageNode {
    cake_1_PlaneNode?.removeFromParentNode()
    detectedImageNode.addChildNode(planeNodee)
}

您的最终代码应如下所示:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    guard let imageAnchor = anchor as? ARImageAnchor else { return }

    if let imageName  = imageAnchor.referenceImage.name {
        print(imageName)

        if imageName == "menu" {

            let cake_1_Plane = SCNPlane(width: 0.045, height: 0.045)
            cake_1_Plane.firstMaterial?.diffuse.contents = UIImage(named: "france")
            cake_1_Plane.cornerRadius = 0.01

            let cake_1_PlaneNode = SCNNode(geometry: cake_1_Plane)
            self.cake_1_PlaneNode = cake_1_PlaneNode
            cake_1_PlaneNode.eulerAngles.x = -.pi/2
            cake_1_PlaneNode.runAction(SCNAction.moveBy(x: 0.15, y: 0, z: -0.125, duration: 0.75))

            node.addChildNode(cake_1_PlaneNode)
            // No need to add the following line. The node is already added to the scene
            //self.sceneView.scene.rootNode.addChildNode(node)                
            detectedImageNode = node
        }
    }
}

func cake_1() {

        let plane = SCNPlane(width: 0.15  , height: 0.15)
        plane.firstMaterial?.diffuse.contents = UIColor.black.withAlphaComponent(0.75)

        let planeNodee = SCNNode(geometry: plane)
        planeNodee.eulerAngles.x = -.pi / 2

        if let detectedImageNode = detectedImageNode {
            cake_1_PlaneNode?.removeFromParentNode()
            detectedImageNode.addChildNode(planeNodee)
        }    
    }

替代解决方案

如果您只是想更改平面的图像,那么更简单的方法就是更改平面的纹理。 将cake_1()的内容替换为:

if let planeGeometry = cake_1_PlaneNode?.geometry {
    planeGeometry.firstMaterial?.diffuse.contents = UIImage(named: "newImage")
}