How to rotate a SCNSphere using a pan gesture recognizer

我与影子孤独终老i 提交于 2019-12-03 20:13:51

We have a ViewController that contains a node sphereNode that contains our sphere. To rotate the sphere we could use a UIPanGestureRecognizer. Since the recognizer reports the total distance our finger has traveled on the screen we cache the last point that was reported to us.

var previousPanPoint: CGPoint?
let pixelToAngleConstant: Float = .pi / 180
func handlePan(_ newPoint: CGPoint) {
    if let previousPoint = previousPanPoint {
        let dx = Float(newPoint.x - previousPoint.x)
        let dy = Float(newPoint.y - previousPoint.y)

        rotateUp(by: dy * pixelToAngleConstant)
        rotateRight(by: dx * pixelToAngleConstant)
    }

    previousPanPoint = newPoint
}

We calculate dx and dy with how much pixel our finger has traveled in each direction since we last called the recognizer. With the pixelToAngleConstant we convert our pixel value in an angle (in randians) to rotate our sphere. Use a bigger constant for a faster rotation.

The gesture recognizer returns a state that we can use to determine if the gesture has started, ended, or the finger has been moved. When the gesture starts we save the fingers location in previousPanPoint. When our finger moves we call the function above. When the gesture is ended or canceled we clear our previousPanPoint.

@objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
    switch gestureRecognizer.state {
    case .began:
        previousPanPoint = gestureRecognizer.location(in: view)
    case .changed:
        handlePan(gestureRecognizer.location(in: view))
    default:
        previousPanPoint = nil
    }
}

How do we rotate our sphere? The functions rotateUp and rotateRight just call our more general function, rotate(by: around:) which accepts not only the angle but also the axis to rotate around. rotateUp rotates around the x-axis, rotateRight around the y-axis.

func rotateUp(by angle: Float) {
    let axis = SCNVector3(1, 0, 0) // x-axis
    rotate(by: angle, around: axis)
}

func rotateRight(by angle: Float) {
    let axis = SCNVector3(0, 1, 0) // y-axis
    rotate(by: angle, around: axis)
}

The rotate(by:around:) is in this case relative simple because we assume that the node is not translated/ we want to rotate around the origin of the nodes local coordinate system. Everything is a little more complicated when we look at a general case but this answer is only a small starting point.

func rotate(by angle: Float, around axis: SCNVector3) {
    let transform = SCNMatrix4MakeRotation(angle, axis.x, axis.y, axis.z)
    sphereNode.transform = SCNMatrix4Mult(sphereNode.transform, transform)
}

We create a rotation matrix from the angle and the axis and multiply the old transform of our sphere with the calculated one to get the new transform.

This is the little demo I created:


This approach has two major downsides.

  1. It only rotates around the nodes coordinate origin and only works properly if the node's position is SCNVector3Zero

  2. It does takes neither the speed of the gesture into account nor does the sphere continue to rotate when the gesture stops. An effect similar to a table view where you can flip your finger and the table view scrolls fast and then slows down can't be easily achieved with this approach. One solution would be to use the physics system for that.

Below is what I tried, not sure whether it is accurate with respect to angles but...it sufficed most of my needs....

@objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) {

    let translation = gestureRecognizer.translation(in: gestureRecognizer.view!)

    let x = Float(translation.x)
    let y = Float(-translation.y)
    let anglePan = (sqrt(pow(x,2)+pow(y,2)))*(Float)(Double.pi)/180.0

    var rotationVector = SCNVector4()
    rotationVector.x = x
    rotationVector.y = y
    rotationVector.z = 0.0
    rotationVector.w = anglePan


    self.earthNode.rotation = rotationVector
}

Sample Github-EarthRotate

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