iosswiftrotationscenekitpanning

SceneKit: move camera towards direction its facing with pan gesture


I have set up some custom camera controls in my SceneKit game. I am having a problem with my pan gesture auto-adapting based on the cameras y euler angle. The pan gesture I have works by panning the camera on the x and z axis (by using the gestures translation) The problem is, despite the cameras rotation, the camera will continue to pan on the x and z axis. I want it so that the camera pans on the axis its facing.

here are my gestures I am using to pan/rotate:

panning:

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

rotation:

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

the CameraHandler Node is the parent node of the Camera Node. It all works, it just doesnt work like I want it to. Hopefully this is clear enough for you guys to understand.


Solution

  • In Objective C. The key part is the last three lines and specifically the order of multiplication of the matrices in the last line (causing the movement to happen in local space). If the transmat and cammat are switched it would behave again like you have now (moving in world space). The refactor part is just something that works for my specific situation where both perspective and orthographic camera is possible.

    -(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);
    
    }