如何使用平移手势识别器旋转SCNSphere [英] How to rotate a SCNSphere using a pan gesture recognizer

查看:72
本文介绍了如何使用平移手势识别器旋转SCNSphere的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  origin = sceneView.frame.originnode.geometry = SCNSphere(半径:1)node.geometry?.firstMaterial?.diffuse.contents = UIImage(名称:"world.jpg")让panGestureRecognizer = UIPanGestureRecognizer(目标:自我,行动​​:#selector(CategoryViewController.panGlobe(sender :)))sceneView.addGestureRecognizer(panGestureRecognizer)func panGlobe(发送方:UIPanGestureRecognizer){//我应该在此方法中放些什么,以使他们旋转球体/球} 

解决方案

我们有一个ViewController,其中包含一个节点 sphereNode ,该节点包含我们的球体.要旋转球体,我们可以使用


此方法有两个主要缺点.

  1. 它仅绕节点坐标原点旋转,并且仅在节点位置为 SCNVector3Zero

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

I created an SCNSphere so now it looks like a planet kind of. This is exactly what I want. My next goal is to allow users to rotate the sphere using a pan gesture recognizer. They are allowed to rotate it around the X or Y axis. I was just wondering how I can do that. This is what I have so far.

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
}

解决方案

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.

这篇关于如何使用平移手势识别器旋转SCNSphere的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆