代码之家  ›  专栏  ›  技术社区  ›  E. Huckabee

SceneKit:使用平移手势将相机朝其面对的方向移动

  •  0
  • E. Huckabee  · 技术社区  · 7 年前

    我在SceneKit游戏中设置了一些自定义相机控件。我有一个基于相机y euler角度的平移姿态自适应问题。我使用的平移手势是在x轴和z轴上平移相机(通过使用手势平移)。问题是,尽管相机旋转,相机仍将在x轴和z轴上继续平移。我想要它使相机在其面对的轴上平移。

    以下是我用来平移/旋转的手势:

    平移:

    var previousTranslation = SCNVector3(x: 0.0,y: 15,z: 0.0)
    var lastWidthRatio:Float = 0
    var angle:Float = 0
    
    @objc func pan(gesture: UIPanGestureRecognizer) {
        gesture.minimumNumberOfTouches = 1
        gesture.maximumNumberOfTouches = 1
        if gesture.numberOfTouches == 1 {
            let view = self.view as! SCNView
            let node = view.scene!.rootNode.childNode(withName: "Node", recursively: false)
            let secondNode =  view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
            let translation = gesture.translation(in: view)
    
            let constant: Float = 30.0
            angle = secondNode!.eulerAngles.y
            //these were the previous values I was using to handle panning, they worked but provided really jittery movement. You can change the direction they rotate by multiplying the sine/cosine .pi values by any integer.
            //var translateX = Float(translation.y) * sin(.pi) / cos(.pi) - Float(translation.x) * cos(.pi)
            //var translateY = Float(translation.y) * cos(.pi) / cos(.pi) + Float(translation.x) * sin(.pi)
    
            //these ones work a lot smoother
            var translateX = Float(translation.x) * Float(Double.pi / 180)
            var translateY = Float(translation.y) * Float(Double.pi / 180)
            translateX = translateX * constant
            translateY = translateY * constant
    
            switch gesture.state {
            case .began:
                previousTranslation = node!.position
                break;
            case .changed:
                node!.position = SCNVector3Make((previousTranslation.x + translateX), previousTranslation.y, (previousTranslation.z + translateY))
                break
            default: break
            }
        }
    }
    

    旋转:

    @objc func rotate(gesture: UIPanGestureRecognizer) {
        gesture.minimumNumberOfTouches = 2
        gesture.maximumNumberOfTouches = 2
        if gesture.numberOfTouches == 2 {
            let view = self.view as! SCNView
            let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
            let translate = gesture.translation(in: view)
    
            var widthRatio:Float = 0
    
            widthRatio = Float(translate.x / 10) * Float(Double.pi / 180)
    
            switch gesture.state {
            case .began:
                lastWidthRatio = node!.eulerAngles.y
                break
            case .changed:
                node!.eulerAngles.y = lastWidthRatio + widthRatio
                print(node!.eulerAngles.y)
                break
            default: break
            }
        }
    }
    

    这个 CameraHandler 节点是 Camera 节点。这一切都起作用了,只是没有像我希望的那样起作用。希望你们能明白这一点。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Xartec    7 年前

    在目标C中,关键部分是最后三行,特别是最后一行中矩阵的乘法顺序(导致局部空间发生移动)。如果transmat和cammat切换,它将再次像现在一样工作(在世界空间中移动)。重构部分只适用于我的特定情况,即透视和正交相机都是可能的。

    -(void)panCamera :(CGPoint)location {
    
    CGFloat dx = _prevlocation.x - location.x;
    CGFloat dy = location.y - _prevlocation.y;
    _prevlocation = location;
    
    //refactor dx and dy based on camera distance or orthoscale
    if (cameraNode.camera.usesOrthographicProjection) {
        dx = dx / 416 * cameraNode.camera.orthographicScale;
        dy = dy / 416 * cameraNode.camera.orthographicScale;
    } else {
        dx = dx / 720 * cameraNode.position.z;
        dy = dy / 720 * cameraNode.position.z;
    }
    
    SCNMatrix4 cammat = self.cameraNode.transform;
    SCNMatrix4 transmat = SCNMatrix4MakeTranslation(dx, 0, dy);
    self.cameraNode.transform = SCNMatrix4Mult(transmat, cammat);
    
    }
    
        2
  •  1
  •   E. Huckabee    6 年前

    根据Xartec的回答,我找到了答案。我将其转换为swift,并对其进行了复古,以满足我的需要。我真的很不满意,因为动作不流畅。今天晚些时候我会把它弄平滑。

    平移手势:该手势沿摄影机旋转的方向围绕场景平移摄影机父节点。它完全符合我的要求。

    @objc func pan(gesture: UIPanGestureRecognizer) {
        gesture.minimumNumberOfTouches = 1
        gesture.maximumNumberOfTouches = 1
        if gesture.numberOfTouches == 1 {
            let view = self.view as! SCNView
            let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
            let translation = gesture.translation(in: view)
    
            var dx = previousTranslation.x - translation.x
            var dy = previousTranslation.y - translation.y
    
            dx = dx / 100
            dy = dy / 100
            print(dx,dy)
    
            let cammat = node!.transform
            let transmat = SCNMatrix4MakeTranslation(Float(dx), 0, Float(dy))
    
            switch gesture.state {
            case .began:
                previousTranslation = translation
                break;
            case .changed:
                node!.transform = SCNMatrix4Mult(transmat, cammat)
                break
            default: break
            }
        }
    }
    

    旋转手势:该手势用两个手指旋转相机。

    @objc func rotate(gesture: UIPanGestureRecognizer) {
        gesture.minimumNumberOfTouches = 2
        gesture.maximumNumberOfTouches = 2
        if gesture.numberOfTouches == 2 {
            let view = self.view as! SCNView
            let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
            let translate = gesture.translation(in: view)
    
            var widthRatio:Float = 0
            widthRatio = Float(translate.x / 10) * Float(Double.pi / 180)
    
            switch gesture.state {
            case .began:
                lastWidthRatio = node!.eulerAngles.y
                break
            case .changed:
                node!.eulerAngles.y = lastWidthRatio + widthRatio
                break
            default: break
            }
        }
    }
    

    要获得与我相同的功能,您需要附加 cameraNode 到父节点。像这样:

        //create and add a camera to the scene
        let cameraNode = SCNNode()
    
        //cameraHandler is declared outside viewDidLoad. 
        let cameraHandler = SCNNode()
    
        cameraNode.camera = SCNCamera()
        cameraNode.name = "Camera"
        cameraNode.position = SCNVector3(x: 0.0,y: 10.0,z: 20.0)
        //This euler angle only rotates the camera downward a little bit. It is not neccessary 
        cameraNode.eulerAngles = SCNVector3(x: -0.6, y: 0, z: 0)
    
        cameraHandler.addChildNode(cameraNode)
        cameraHandler.name = "CameraHandler"
    
        scene.rootNode.addChildNode(cameraHandler)