【问题标题】:How to rotate a SCNSphere using a pan gesture recognizer如何使用平移手势识别器旋转 SCNSphere
【发布时间】:2018-02-01 17:56:17
【问题描述】:

我创建了一个 SCNSphere,所以现在它看起来有点像行星。这正是我想要的。我的下一个目标是允许用户使用平移手势识别器旋转球体。他们可以围绕 X 或 Y 轴旋转它。我只是想知道我该怎么做。这是我目前所拥有的。

origin = sceneView.frame.origin
node.geometry = SCNSphere(radius: 1)
node.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "world.jpg")

let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(CategoryViewController.panGlobe(sender:)))
sceneView.addGestureRecognizer(panGestureRecognizer)

func panGlobe(sender: UIPanGestureRecognizer) {
    // What should i put inside this method to allow them to rotate the sphere/ball
}

【问题讨论】:

  • 为什么要在场景视图中添加手势?搜索 hitTest。我可以告诉你这并不容易。
  • 因为场景视图包含一个球。所以我只希望手势应用于球/球体
  • 好的,我该怎么办?

标签: ios swift scenekit uipangesturerecognizer


【解决方案1】:

我们有一个 ViewController,其中包含一个节点 sphereNode,其中包含我们的球体。 要旋转球体,我们可以使用UIPanGestureRecognizer。 由于识别器报告了我们的手指在屏幕上移动的总距离,我们缓存了报告给我们的最后一个点。

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
}

我们计算dxdy 自上次调用识别器以来手指在每个方向上移动了多少像素。 使用pixelToAngleConstant,我们将像素值转换为角度(以弧度为单位)来旋转我们的球体。使用更大的常数以获得更快的旋转。

手势识别器返回一个state,我们可以使用它来确定手势是开始、结束还是手指已经移动。 当手势开始时,我们将手指位置保存在previousPanPoint 中。 当我们的手指移动时,我们调用上面的函数。 当手势结束或取消时,我们清除我们的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
    }
}

我们如何旋转我们的球体? 函数rotateUprotateRight 只是调用我们更通用的函数rotate(by: around:),它不仅接受角度,还接受要旋转的轴。 rotateUp 绕 x 轴旋转,rotateRight 绕 y 轴旋转。

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

rotate(by:around:) 在这种情况下相对简单,因为我们假设节点没有平移/我们想围绕节点局部坐标系的原点旋转。 当我们看一个一般情况时,一切都会稍微复杂一些,但这个答案只是一个很小的起点。

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

我们从angleaxis 创建一个旋转矩阵,并将我们球体的旧transform 乘以计算得到的新transform

这是我创建的小演示:


这种方法有两个主要缺点。

  1. 它只围绕节点坐标原点旋转,只有在节点位置为SCNVector3Zero时才能正常工作

  2. 它既不考虑手势的速度,也不考虑手势停止时球体继续旋转。 使用这种方法无法轻松实现类似于表格视图的效果,您可以在其中翻转手指并且表格视图快速滚动然后放慢速度。 一种解决方案是为此使用物理系统。

【讨论】:

    【解决方案2】:

    以下是我尝试过的,不确定它在角度方面是否准确,但......它满足了我的大部分需求......

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

    样本Github-EarthRotate

    【讨论】:

      猜你喜欢
      • 2015-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      相关资源
      最近更新 更多