SceneKit - 通过平移手势使球体旋转时添加漂移效果

4
我正在使用SceneKit,通过滑动手势让用户旋转他们在球体内所看到的内容。这个功能很好用 - 但是我想让球体保持稍微朝着滑动手势的方向旋转,以获得更好的反应感。
以下是我的场景设置:
    // Create scene
    let scene = SCNScene()

    sceneView.scene = scene

    let panRecognizer = UIPanGestureRecognizer(target: self,
                                               action: #selector(ViewController.handlePanGesture(_:)))

    sceneView.addGestureRecognizer(panRecognizer)

    //Create sphere
    let sphere = SCNSphere(radius: 50.0)

    // sphere setup

    sphereNode = SCNNode(geometry: sphere)

    sphereNode!.position = SCNVector3Make(0,0,0)
    scene.rootNode.addChildNode(sphereNode!)

    // Create camera
    let camera = SCNCamera()

    // camera setup

    cameraNode = SCNNode()

    cameraNode!.camera = camera
    cameraNode!.position = SCNVector3Make(0, 0, 0)

    cameraOrbit = SCNNode()

    cameraOrbit!.addChildNode(cameraNode!)
    scene.rootNode.addChildNode(cameraOrbit!)

    let lookAtConstraint = SCNLookAtConstraint(target: sphereNode!)
    lookAtConstraint.gimbalLockEnabled = true
    cameraNode!.constraints = [lookAtConstraint]

    sceneView.pointOfView = cameraNode

这里是当前处理平移手势的方式(由SCNCamera limit arcball rotation提供):
    let translation = sender.translationInView(sender.view!)
    let widthRatio = Float(translation.x) / Float(sender.view!.frame.size.width) + lastWidthRatio
    let heightRatio = Float(translation.y) / Float(sender.view!.frame.size.height) + lastHeightRatio

    self.cameraOrbit?.eulerAngles.y = Float(-2 * M_PI) * widthRatio
    self.cameraOrbit?.eulerAngles.x = Float(-M_PI) * heightRatio

    if (sender.state == .Ended) {
        lastWidthRatio = widthRatio
        lastHeightRatio = heightRatio
    }

我不确定如何开始添加轻微连续旋转的动作。也许添加一个physicsBody 并施加力?或者动画改变eulerAngles的值?

1个回答

5

我之前处理类似问题的方式是将手势处理程序分成手势状态的开始、更改和结束部分。如果旋转对您有效,您只需要在if (sender.state == .Ended)语句的底部添加代码。

lastHeightRatio = heightRatio之后,您可以使用sender.velocityInView(sender.view!)在视图中找到平移速度,然后像以前一样找到水平分量,然后添加一个SCNAction来旋转节点,并使用EaseOut时间模式。

您可能需要一个乘数,将视图中的速度(以点为单位)转换为您希望通过旋转旋转的角度(以弧度为单位)。相对较小的10个点的速度将导致非常快的旋转。

此外,如果您希望触摸停止残留旋转,您需要在手势重新开始时从节点中删除所有操作。

以下是一些示例代码:

if (sender.state == .Ended) {
  lastWidthRatio = widthRatio
  lastHeightRatio = heightRatio

  // Find velocity
  let velocity = sender.velocityInView(sender.view!)

  // Create Action for whichever axis is the correct one to rotate about.
  // The 0.00001 is just a factor to provide the rotation speed to pan
  // speed ratio you'd like. Play with this number and the duration to get the result you want
  let rotate = SCNAction.rotateByX(velocity.x * 0.00001, y: 0, z: 0, duration: 2.0)

  // Run the action on the camera orbit node (if I understand your setup correctly)
  cameraOrbit.runAction(rotate)
}

这应该足够让您开始了解。

我最终做了类似的事情 - 谢谢!我的问题现在是,lastRatio值现在不准确了,因为球体的最终静止位置已经旋转超过上次设置该值的位置。所以,当用户进行下一个平移手势时,相机会“跳到”漂移开始之前的位置,因为eulerAngles被重置了。如果您对此有任何想法,我全听着。 - H K
可能尝试向runAction添加完成处理程序,当动作完成时更新lastRatio值? - Andy Heard
@AndyHeard 我试过了,但它不起作用,我真的不明白为什么。这是我的代码片段:lastWidthRatio += (((-velocity.x * 0.001)*2) / sender.view.frame.size.width); - Kevin Goedecke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接