iOS 11 ARKit : Drag Object in 3D View

六眼飞鱼酱① 提交于 2020-01-03 01:46:23

问题


I have a node object in 3d view and i need to drag that object, So far i have tried from here : Placing, Dragging and Removing SCNNodes in ARKit

and converted in swift

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

but it is not working properly. what is the correct way to do?


回答1:


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
    }

} 



回答2:


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


来源:https://stackoverflow.com/questions/48131505/ios-11-arkit-drag-object-in-3d-view

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