在 XZ 平面上拖动对象

Drag object on XZ plane

我正在开发增强现实应用程序,我希望能够在 space 中拖动对象。我在 SO 中找到的解决方案的问题是,建议使用 projectPoint/unprojectPoint 的解决方案是它们沿着 XY 平面产生运动。

我试图使用手指在屏幕上的移动作为节点 xz 坐标的偏移量。问题是有很多东西需要考虑(相机的位置、节点的位置、节点的旋转等等)

有更简单的方法吗?

如果我没理解错的话,我是使用添加到 ARSCNView 的 UIPanGestureRecognizer 来执行此操作的。

在我的例子中,我想检查是否在给定的虚拟对象上启动了平移并跟踪它是哪个,因为我可以有多个,但如果你只有一个对象,你可能不需要 targetNode 变量。 我用来除法的 700 常量是通过反复试验得到的,以使翻译更流畅,您可能需要根据您的情况进行更改。

向上移动手指,使物体远离相机,向下移动手指,使物体离相机更近。手指的水平移动移动对象 left/right.

@objc func onTranslate(_ sender: UIPanGestureRecognizer) {
    let position = sender.location(in: scnView)
    let state = sender.state

    if (state == .failed || state == .cancelled) {
        return
    }

    if (state == .began) {
        // Check pan began on a virtual object
        if let objectNode = virtualObject(at: position).node {
            targetNode = objectNode
            latestTranslatePos = position
        }
    }
    else if let _ = targetNode {

        // Translate virtual object
        let deltaX = Float(position.x - latestTranslatePos!.x)/700
        let deltaY = Float(position.y - latestTranslatePos!.y)/700

        targetNode!.localTranslate(by: SCNVector3Make(deltaX, 0.0, deltaY))
        latestTranslatePos = position

        if (state == .ended) {
            targetNode = nil
        }
    }

首先,您需要在原点下方几米(我有 10 米)处创建地板或非常大的平面。这可确保您的命中率始终为 returns 值。然后使用平移手势:

//store previous coordinates from hittest to compare with current ones
var PCoordx: Float = 0.0
var PCoordz: Float = 0.0

@objc func move(_ gestureRecognizer: UIPanGestureRecognizer){
    if gestureRecognizer.state == .began{
        let hitNode = sceneView.hitTest(gestureRecognizer.location(in: sceneView), options: nil)
        PCoordx = (hitNode.first?.worldCoordinates.x)!
        PCoordz = (hitNode.first?.worldCoordinates.z)!
    }

    // 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 
    if gestureRecognizer.state == .changed {
        let hitNode = sceneView.hitTest(gestureRecognizer.location(in: sceneView), options: nil)
        if let coordx = hitNode.first?.worldCoordinates.x{
            if let coordz = hitNode.first?.worldCoordinates.z{

            let action = SCNAction.moveBy(x: CGFloat(coordx-PCoordx), y: 0, z: CGFloat(coordz-PCoordz), duration: 0.1)
            node.runAction(action)

            PCoordx = coordx
            PCoordz = coordz
            }
        }

        gestureRecognizer.setTranslation(CGPoint.zero, in: sceneView)
    }
    if gestureRecognizer.state == .ended{
        PCoordx = 0
        PCoordz = 0
    }
}

在我的例子中只有一个节点,所以我没有检查是否录制了所需的节点。如果你有很多节点,你可以随时检查它。

我已经更新了 @Alok 的答案,因为在我的例子中,它只是从上面的解决方案中拖入 x 平面。所以我添加了 y 坐标,为我工作。

var PCoordx: Float = 0.0
var PCoordy: Float = 0.0
var PCoordz: Float = 0.0

@objc func handleDragGesture(_ sender: UIPanGestureRecognizer) {

    switch sender.state {
    case .began:
        let hitNode = self.sceneView.hitTest(sender.location(in: self.sceneView),
                                             options: nil)
        self.PCoordx = (hitNode.first?.worldCoordinates.x)!
        self.PCoordy = (hitNode.first?.worldCoordinates.y)!
        self.PCoordz = (hitNode.first?.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 - PCoordx),
                                          y: CGFloat(coordy - PCoordy),
                                          z: CGFloat(coordz - 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
    }
}