如何使用平移手势识别器旋转SCNSphere

我创建了一个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 } 

我们有一个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我们将角度(在randians中)转换为像素值以旋转我们的球体。 使用更大的常数来加快旋转速度。

手势识别器返回一个state ,我们可以使用该state来确定手势是否已经开始,结束或手指是否已被移动。 手势开始时,我们将手指位置保存在previousPanPoint 。 当我们的手指移动时,我们称之为上述function。 当手势结束或取消时,我们清除我们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. 它既不考虑手势的速度,也不会在手势停止时球体继续旋转。 使用此方法无法轻松实现类似于桌面视图的效果,在该视图中您可以翻转手指并快速滚动表格视图然后减速。 一种解决方案是使用物理系统。

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

 @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

用手势旋转地球