在ARKit中使用SceneKit拖动SCNNode

13
我在ARKit中有一个简单的SCNNode,我想让它跟随我的手指移动。这是我的代码。
 @objc func pan(recognizer :UIGestureRecognizer) {

        guard let currentFrame = self.sceneView.session.currentFrame else {
            return
        }

        var translation = matrix_identity_float4x4
        translation.columns.3.z = -1.5

        let sceneView = recognizer.view as! ARSCNView
        let touchLocation = recognizer.location(in: sceneView)

        let hitTestResult = sceneView.hitTest(touchLocation, options: [:])

        if !hitTestResult.isEmpty {

            print("hit result")

            guard let hitResult = hitTestResult.first else {
                return
            }

            let node = hitResult.node

            node.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
        }
    }

问题在于拖动非常缓慢且不流畅。

请查看以下问题:https://dev59.com/lmw15IYBdhLWcg3wWqV0。您应该正确处理平移手势的状态。 - John Tracid
这个对你有用吗? - mergesort
约翰·多,不知道你是否已经解决了什么问题。 - Alan
你实现了这个吗?我正好也在寻找同样的东西。 - yaali
可能是[ARKIT:使用PanGesture移动对象(正确的方法)]的重复问题(https://dev59.com/TVUL5IYBdhLWcg3wlI1w)。 - Susan Kim
请查看我对@SusanKim链接的帖子的回答,并在那里作为评论发布任何进一步的问题,因为这应该是拖动对象的相对较好的方法。 - A. Claesson
3个回答

7
我这样处理PanGesture的翻译。除以700是为了平滑和调整移动速度,我通过试错得出了该值,您可能想要进行实验。
@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 it's on a virtual object
        if let objectNode = virtualObject(at: position) {
            // virtualObject(at searches for root node if it's a subnode
            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
        }
    }
}

这个在两个轴上是有效的,但当我使用UIRotationGestureRecognizer旋转物体并尝试移动它时,它不能正确地工作。当我从左到右移动手指时,SCNode会朝相反的方向移动。有没有一种方法可以使SCNode根据手指的移动正确地移动,而不受旋转的影响? - yaali
嗨@yaali,我只是猜测一下,也许如果你使用一些可以改变相对于父节点或世界原点的位置的东西,你就可以纠正它,而不是使用 localTranslate:by。也许是 worldPositionposition - leandrodemarco
@leandrodemarco:你能帮我理解一下“*virtualObject(at: position)*”是什么以及它的作用吗? - Wolverine
虚拟对象是什么? - Abhishek Thapliyal
仍然像冠军一样运行!谢谢! - Joel

1

我遇到了同样的问题。使用SCNTransaction解决了我的问题。

@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
    [...]

    SCNTransaction.begin()
    SCNTransaction.animationDuration = 0
    imagePlane.position.x = hitTestResult.localCoordinates.x
    imagePlane.position.y = hitTestResult.localCoordinates.y
    SCNTransaction.commit()
}

0

我曾经遇到过类似的问题。虽然你应该按照评论中John的建议使用正确的平移手势状态(Began,Changed,Ended),但我认为问题可能是你正在获取hitResult.node而不是获取节点的父级,甚至是父级的父级等等...我也遇到了这个问题,并最终通过向上获取父级直到整个对象被选择而不是其中一部分来解决它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接