iOS 11 ARKit : Drag Object in 3D View

老子叫甜甜 提交于 2019-12-07 03:01:28

You can do this using panGestureRecongniser... see basic swift Playground code for handling a SCNNode.

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 

EDIT:

The above drag function only worked if you had 1 object in the view, so it was not really necessary to hit the node to start dragging. It will just drag from where ever you tapped on the screen. If you have multiple objects in the view, and you want to drag nodes independently. You could change the panGesture function to the following, detect each node tapped first:

// drags nodes independently

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

} 

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

This code works for me

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
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!