簡體   English   中英

如何使用平移手勢識別器旋轉SCNSphere

[英]How to rotate a SCNSphere using a pan gesture recognizer

我創建了一個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 當我們的手指移動時,我們調用上面的函數。 當手勢結束或取消時,我們清除我們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相乘以得到新的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

用手勢旋轉地球

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM