iOS 11 ARKit:在3D视图中拖动对象

时间:2018-01-06 20:18:03

标签: ios swift3 arkit

我在3d视图中有一个节点对象,我需要拖动该对象, 到目前为止,我已经尝试过这里:Placing, Dragging and Removing SCNNodes in ARKit

并快速转换

@objc func handleDragGesture(_ gestureRecognizer: UIGestureRecognizer) {
        let tapPoint = gestureRecognizer.location(in: self.sceneView)
        switch gestureRecognizer.state {
        case .began:
            print("Object began to move")
            let hitResults = self.sceneView.hitTest(tapPoint, options: nil)
            if hitResults.isEmpty { return }
            let hitResult = hitResults.first
            if let node = hitResult?.node.parent?.parent?.parent {
                self.photoNode = node
            }
        case .changed:
            print("Moving object position changed")
            if let _ = self.photoNode {
                let hitResults = self.sceneView.hitTest(tapPoint, types: .featurePoint)
                let hitResult = hitResults.last
                if let transform = hitResult?.worldTransform {
                    let matrix = SCNMatrix4FromMat4(transform)
                    let vector = SCNVector3Make(matrix.m41, matrix.m42, matrix.m43)
                    self.photoNode?.position = vector
                }
            }
        case .ended:
            print("Done moving object")
        default:
            break
        }
    }

但它无法正常工作。什么是正确的方法?

2 个答案:

答案 0 :(得分:3)

您可以使用panGestureRecongniser执行此操作...请参阅用于处理SCNNode的基本swift Playground代码。

import UIKit
import ARKit
import SceneKit
import PlaygroundSupport

public var textNode : SCNNode?

// Main ARKIT ViewController
class ViewController : UIViewController, ARSCNViewDelegate, ARSessionDelegate {

var textNode: SCNNode!
var counter = 0
@IBOutlet var sceneView: ARSCNView!

override func viewDidLoad() {
    super.viewDidLoad()
    // set the views delegate
    sceneView.delegate = self as! ARSCNViewDelegate
    // show statistics such as fps and timing information
    sceneView.showsStatistics = true
    // Create a new scene 
    sceneView.scene.rootNode
    // Add ligthing
    sceneView.autoenablesDefaultLighting = true

    let text = SCNText(string: "Drag Me with Pan Gesture!", extrusionDepth: 1)
    //  create material
    let material = SCNMaterial()
    material.diffuse.contents = UIColor.green
    text.materials = [material]

    //Create Node object
    textNode = SCNNode()

    textNode.name =  "textNode"
    textNode.scale = SCNVector3(x:0.004,y:0.004,z:0.004)
    textNode.geometry = text
    textNode.position = SCNVector3(x: 0, y:0.02, z: -1)

    //  add new node to root node
    self.sceneView.scene.rootNode.addChildNode(textNode)

    // Add pan gesture for dragging the textNode about
    sceneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(panGesture(_:))))

}

override func loadView() {

    sceneView = ARSCNView(frame:CGRect(x: 0.0, y: 0.0, width: 500.0, height: 600.0))
    // Set the view's delegate
    sceneView.delegate = self 

    let config = ARWorldTrackingConfiguration()
    config.planeDetection = .horizontal

    // Now we'll get messages when planes were detected...
    sceneView.session.delegate = self

    self.view = sceneView
    sceneView.session.run(config)

}

@objc func panGesture(_ gesture: UIPanGestureRecognizer) {     

    gesture.minimumNumberOfTouches = 1

    let results = self.sceneView.hitTest(gesture.location(in: gesture.view), types: ARHitTestResult.ResultType.featurePoint)
    guard let result: ARHitTestResult = results.first else {
        return
    }

    let position = SCNVector3Make(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z)
    textNode.position = position
} 

}

PlaygroundPage.current.liveView = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true 

编辑:

上述拖动功能仅在视图中有1个对象时才有效,因此没有必要点击节点开始拖动。它只会拖动您点击屏幕的位置。如果视图中有多个对象,并且您希望单独拖动节点。您可以将panGesture功能更改为以下内容,检测首先点击的每个节点:

//独立拖动节点

@objc func panGesture(_ gesture: UIPanGestureRecognizer) {     

    gesture.minimumNumberOfTouches = 1

    let results = self.sceneView.hitTest(gesture.location(in: gesture.view), types: ARHitTestResult.ResultType.featurePoint)

    guard let result: ARHitTestResult = results.first else {
        return
    }

    let hits = self.sceneView.hitTest(gesture.location(in: gesture.view), options: nil)
    if let tappedNode = hits.first?.node {
        let position = SCNVector3Make(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z)
        tappedNode.position = position
    }

} 

答案 1 :(得分:0)

REF: https://stackoverflow.com/a/48220751/5589073

此代码对我有用

private func drag(sender: UIPanGestureRecognizer) {

    switch sender.state {
    case .began:
        let location = sender.location(in: self.sceneView)
        guard let hitNodeResult = self.sceneView.hitTest(location,
                                                         options: nil).first else { return }
        self.PCoordx = hitNodeResult.worldCoordinates.x
        self.PCoordy = hitNodeResult.worldCoordinates.y
        self.PCoordz = hitNodeResult.worldCoordinates.z
    case .changed:
        // when you start to pan in screen with your finger
        // hittest gives new coordinates of touched location in sceneView
        // coord-pcoord gives distance to move or distance paned in sceneview
        let hitNode = sceneView.hitTest(sender.location(in: sceneView), options: nil)
        if let coordx = hitNode.first?.worldCoordinates.x,
            let coordy = hitNode.first?.worldCoordinates.y,
            let coordz = hitNode.first?.worldCoordinates.z {
            let action = SCNAction.moveBy(x: CGFloat(coordx - self.PCoordx),
                                          y: CGFloat(coordy - self.PCoordy),
                                          z: CGFloat(coordz - self.PCoordz),
                                          duration: 0.0)
            self.photoNode.runAction(action)

            self.PCoordx = coordx
            self.PCoordy = coordy
            self.PCoordz = coordz
        }

        sender.setTranslation(CGPoint.zero, in: self.sceneView)
    case .ended:
        self.PCoordx = 0.0
        self.PCoordy = 0.0
        self.PCoordz = 0.0
    default:
        break
    }
}
相关问题