Как обнаружить касание и показать новый SCNPlane с помощью ARKit? - PullRequest
0 голосов
/ 30 августа 2018

Теперь я могу показывать разные 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

Перейдите по этой ссылке: Обнаружение касания на SCNNode в ARKit .

Ответы [ 2 ]

0 голосов
/ 01 сентября 2018

*** Пожалуйста, используйте описательные и понятные имена для ваших переменных и функций, очень трудно прочитать и понять ваш код. Вы можете прочитать больше о правилах быстрой укладки здесь: https://github.com/raywenderlich/swift-style-guide#naming

Вы создаете новую плоскость, когда пользователь касается экрана, но вы не добавляете эту плоскость в сцену, поэтому ваша функция "cake_1 ()" создает только новую плоскость. Когда ARKit обнаруживает изображение, оно автоматически создает пустой узел и добавляет его к нашей сцене, в центре обнаруженного изображения. Сначала мы должны сохранить ссылку на узел, который ARKit добавил для нас при обнаружении изображения. Добавьте эту переменную в начало вашего класса:

var detectedImageNode: SCNNode?

Затем в func renderer (renderer: didAdd node :, for anchor :) добавьте следующую строку:

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")
}
0 голосов
/ 30 августа 2018

Глядя на ваш код, я вижу несколько проблем (не говоря уже о соглашениях по присвоению имен для ваших переменных и методов).

Во-первых, вы создаете 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 вашего ARSCNView, а не к обнаруженному ARImageAnchor, что, вероятно, вам не нужно делать, поскольку при обнаружении ARAnchor:

Вы можете предоставить визуальный контент для якоря, добавив геометрию (или другие функции SceneKit) к этому узлу или путем добавления дочерних узлов.

Таким образом, этот метод (если вы на самом деле не хотите делать это так) не нужен.

Остальные проблемы заключаются в самой функции cake_1.

Во-первых, вы на самом деле не добавляете свой planeNodee к своему sceneHierachy.

Поскольку вы не указали, следует ли добавить только что инициализированный planeNode непосредственно к вашему ARSCNView или как childNode вашего cake_1_planeNode, ваш function должен включать одно из следующих:

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

Кроме того, вероятно, нет необходимости поворачивать planeNodee, поскольку по умолчанию SCNPlane отображается вертикально.

Поскольку вы не указали, где будете размещать свой контент, возможно, использование -.pi / 2 не нужно, поскольку это может сделать его практически невидимым для невооруженного глаза.

Еще одна проблема, которая может также объяснить, что вы не видите свой узел, когда вы фактически добавляете его, это позиция Z.

Если вы установите 2 узла в одной позиции, вы, скорее всего, столкнетесь с проблемой, известной как Z-fighting (подробнее о здесь вы можете прочитать ). Таким образом, вы, вероятно, должны немного переместить добавленный узел вперед, например, добавив его. 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

Для вашей информации, это, кажется, показывает, что ваши расчеты по размещению вашего контента отключены (если, конечно, это не желаемый результат).

Как видите, весь контент отображается правильно, однако расстояние между ними было довольно большим, и поэтому вам, скорее всего, придется немного панорамировать ваше устройство, чтобы увидеть все это при тестировании и дальнейшей разработке.

Надеюсь, это поможет ...

...