代码之家  ›  专栏  ›  技术社区  ›  PvDev

如何使用ARKit检测触摸并显示新屏幕?

  •  6
  • PvDev  · 技术社区  · 6 年前

    SCNPlane ,当检测到卡时。显示后 SCNPlanes ,用户触摸任何平面以显示新的 . 但现在触摸正常工作,但新的 SCN平面 没有显示。

    以下是我尝试过的代码:

    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 回复  |  直到 6 年前
        1
  •  6
  •   BlackMirrorz    6 年前

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

    首先,你在创造一个 Global Variable

    var cake_1_PlaneNode : SCNNode? = nil
    

    Local 全局变量 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)
    

    第二,你正在添加你的 蛋糕\u 1 \u平面节点 rootNode 你的 ARSCNView 而不是你的 ARImageAnchor 这可能是你不想做的,因为 ARAnchor

    可以通过附着几何图形为锚定提供视觉内容

    因此,这种方法(除非你真的想这样做)是不必要的。

    cake_1 功能本身。

    首先,你并不是真的在添加你的 planeNodee 给你的 sceneHierachy

    因为你还没有指定 planeNode ARSCNView公司 childNode 你的 cake_1_planeNode 你的 function 应包括以下内容之一:

    self.sceneView.scene.rootNode.addChildNode(planeNodee)    
    self.cake_1_planeNode.addChildNode(planeNodee)
    

    平面节点 因为默认情况下 SCNPlane 垂直渲染。

    因为你还没有规定你将在哪里放置你的内容,它可能是使用 -.pi / 2

    另一个问题,也可以解释你没有看到你的节点,当你实际添加它,是Z位置。

    Z-fighting (您可以阅读更多 here ). 因此,在添加节点时,您可能应该稍微向前移动添加的节点。 SCNVector3 (0,0,0.001) 来解释这个。

    基于所有这些观点,我提供了一个完整的工作和评论的例子如下:

    import UIKit
    import ARKit
    
    //-------------------------
    //MARK: - ARSCNViewDelegate
    //-------------------------
    
    extension ViewController: ARSCNViewDelegate {
    
        func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    
            //1. Check We Have An ARImageAnchor, Then Get It's Reference Image & Name
            guard let imageAnchor = anchor as? ARImageAnchor else { return }
    
            let detectedTarget = imageAnchor.referenceImage
    
            guard let detectedTargetName = detectedTarget.name  else { return }
    
            //2. If We Have Detected Our Virtual Menu Then Add The CakeOnePlane
            if detectedTargetName == "cakeMenu" {
    
                let cakeOnePlaneGeometry = SCNPlane(width: 0.045, height: 0.045)
                cakeOnePlaneGeometry.firstMaterial?.diffuse.contents = UIColor.cyan
                cakeOnePlaneGeometry.cornerRadius = 0.01
    
                let cakeOnPlaneNode = SCNNode(geometry: cakeOnePlaneGeometry)
                cakeOnPlaneNode.eulerAngles.x = -.pi/2
    
                //3. To Allow Us To Easily Keep Track Our Our Currently Added Node We Will Assign It A Unique Name
                cakeOnPlaneNode.name = "Strawberry Cake"
    
                node.addChildNode(cakeOnPlaneNode)
                cakeOnPlaneNode.runAction(SCNAction.moveBy(x: 0.15, y: 0, z: 0, duration: 0.75))
    
    
            }
        }
    }
    
    class ViewController: UIViewController {
    
        @IBOutlet var augmentedRealityView: ARSCNView!
        let augmentedRealitySession = ARSession()
        let configuration = ARWorldTrackingConfiguration()
    
        //------------------
        //MARK: - Life Cycle
        //------------------
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            setupARSession()
        }
    
    
        //-----------------
        //MARK: - ARSession
        //-----------------
    
        /// Runs The ARSession
        func setupARSession(){
    
            //1. Load Our Detection Images
            guard let detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else { return }
    
            //2. Configure & Run Our ARSession
            augmentedRealityView.session = augmentedRealitySession
            augmentedRealityView.delegate = self
            configuration.detectionImages = detectionImages
            augmentedRealitySession.run(configuration, options: [.resetTracking, .removeExistingAnchors])
    
        }
    
        //--------------------
        //MARK: - Interaction
        //--------------------
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
            //1. Get The Current Touch Location & Perform An ARSCNHitTest To Check For Any Hit SCNNode's
            guard let currentTouchLocation = touches.first?.location(in: self.augmentedRealityView),
                 let hitTestNode = self.augmentedRealityView.hitTest(currentTouchLocation, options: nil).first?.node else { return }
    
            //2. If We Have Hit Our Strawberry Cake Then We Call Our makeCakeOnNode Function
            if let cakeID = hitTestNode.name, cakeID == "Strawberry Cake"{
    
                makeCakeOnNode(hitTestNode)
    
            }
        }
    
    
        /// Adds An SCNPlane To A Detected Cake Target
        ///
        /// - Parameter node: SCNNode
        func makeCakeOnNode(_ node: SCNNode){
    
            let planeGeometry = SCNPlane(width: 0.15  , height: 0.15)
            planeGeometry.firstMaterial?.diffuse.contents = UIColor.black.withAlphaComponent(0.75)
    
            let planeNode = SCNNode(geometry: planeGeometry)
            planeNode.position = SCNVector3(0, 0, 0.001)
            planeNode.runAction(SCNAction.moveBy(x: 0.21, y: 0, z: 0, duration: 0))
            node.addChildNode(planeNode)
    
        }
    }
    

    在我的设备上产生以下结果:

    enter image description here

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

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

    希望对你有帮助。。。

        2
  •  3
  •   SilentK Krunal    6 年前

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

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

    var detectedImageNode: SCNNode?
    

    然后在func renderer(renderer:didAdd node:,for anchor:)中添加以下行:

    detectedImageNode = node
    

    现在我们有了对节点的引用,我们可以轻松地添加和删除其他节点。

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

    替代解决方案

    如果你只是想改变平面的图像,那么更简单的方法就是改变平面的纹理。 将蛋糕\u 1()的内容替换为:

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