簡體   English   中英

SceneKit - 根據方向旋轉帶有平移手勢的SCNNode

[英]SceneKit - rotate SCNNode with pan gesture based on direction

我正在編寫我的第一個場景包項目,我正在嘗試使用平移手勢來旋轉場景中的一個簡單對象(對象是從.dae文件導入的簡單L立方體形狀,正確設置了軸點)。

我經歷了多個SO解決方案和教程,並且我已經整理了一些代碼,但輪換工作不正常。 如果我反復嘗試沿一個軸旋轉對象,它可以正常工作,但是當我嘗試另一個方向時,在平移開始時,對象重置為它的初始位置。 有時旋轉也會隨機怪癖或跳躍。 我不確定我是否使用了正確的方法,請指教..這是我的代碼:

func handlePan(sender: UIPanGestureRecognizer){

    // determine pan direction
    let velocity: CGPoint = sender.velocity(in: sender.view!)
    if self.panDirection == nil {
        if velocity.x > 0 && velocity.x > abs(velocity.y) { self.panDirection = "right" }
        if velocity.x < 0 && abs(velocity.x) > abs(velocity.y) { self.panDirection = "left" }
        if velocity.y < 0 && abs(velocity.y) > abs(velocity.x) { self.panDirection = "up" }
        if velocity.y > 0 &&  velocity.y  > abs(velocity.x) { self.panDirection = "down" }
    }

    // do rotation only on selected SCNNode
    if self.selectedBrickNode != nil {

        // start of pan gesture
        if sender.state == UIGestureRecognizerState.began{
            // remember initial rotation angle
            self.initRot = self.selectedBrickNode.rotation.w
        }

        let translation = sender.translation(in: sender.view!)
        let pan_x =  Float(translation.x)
        let pan_y =  Float(-translation.y)

        // add rotation angle to initial rotation
        var anglePan = self.initRot + (Float)(sqrt(pow(pan_x,2)+pow(pan_y,2)))*(Float)(Double.pi)/180.0
        var rotVector = SCNVector4()

        // if left/right, rotate on Y axis
        rotVector.x = (self.panDirection == "left" || self.panDirection == "right" ) ? 0 : -pan_y
        // if up/down, rotate on X axis
        rotVector.y = (self.panDirection == "up" || self.panDirection == "down" ) ? 0 : pan_x
        rotVector.z = 0
        rotVector.w = anglePan

        // set SCNNode's rotation
        self.selectedBrickNode.rotation = rotVector

        // end of pan gesture
        if(sender.state == UIGestureRecognizerState.ended) {

            // reset initial rotation 
            self.initRot = 0.0   

            // calculate degrees so we can snap to 90deg increments
            var angle = anglePan * (Float) (180.0 /  Double.pi)

            // snap to 90deg increments
            let diff = angle.truncatingRemainder(dividingBy: 90.0)
            if diff <= 45 {
                angle = angle - diff
            }else{
                angle = (angle - diff ) + 90
            }

            // set new rotation to snap
            rotVector.w = angle * (Float)(Double.pi)/180.0
            self.selectedBrickNode.rotation = rotVector
            self.selectedBrickNode = nil
        }
    }


}

經過大量的研究和幾個小時的敲擊牆,我最終找到了一個有效的解決方案,希望它可以幫助有類似目標的人。

我的工作代碼:

func handlePan(sender: UIPanGestureRecognizer){

    // get pan direction
    let velocity: CGPoint = sender.velocity(in: sender.view!)
    if self.panDirection == nil {
        self.panDirection = GameHelper.getPanDirection(velocity: velocity)
    }

    // if selected brick
    if self.selectedBrickNode != nil {

        if sender.state == UIGestureRecognizerState.began{
            lastAngle = 0.0   // reset last angle
        }

        let translation = sender.translation(in: sender.view!)
        let anglePan = (self.panDirection == "horizontal") ?  GameHelper.deg2rad(deg: Float(translation.x)) : GameHelper.deg2rad(deg: Float(translation.y))

        let x:Float = (self.panDirection == "vertical" ) ? 1 : 0.0
        let y:Float = (self.panDirection == "horizontal" ) ?  1 : 0.0

        // calculate the angle change from last call
        let fraction = anglePan - lastAngle
        lastAngle = anglePan

        // perform rotation by difference to last angle
        selectedBrickNode.transform = SCNMatrix4Mult(selectedBrickNode.transform,SCNMatrix4MakeRotation(fraction,  x, y, 0.0))


        if(sender.state == UIGestureRecognizerState.ended) {

            // calculate angle to snap to 90 degree increments
            let finalRotation = GameHelper.rad2deg(rad:anglePan)
            let diff = finalRotation.truncatingRemainder(dividingBy: 90.0)
            var finalDiff = Float(0.0)

            switch diff {
                case 45..<90 :
                    finalDiff = 90 - diff
                case 0..<45 :
                    finalDiff = -diff
                case -45..<0 :
                    finalDiff = abs(diff)
                case -90 ..< -45 :
                    finalDiff = -90 - diff
            default:
                print("do nothing")
            }

            // final transform to apply snap to closest 90deg increment
            let snapAngle = GameHelper.deg2rad(deg: finalDiff)
            selectedBrickNode.transform = SCNMatrix4Mult(selectedBrickNode.transform, SCNMatrix4MakeRotation(snapAngle, x, y, 0.0))

            // remove highlight from node and deselect
            self.selectedBrickNode?.geometry?.materials = [hlp.defaultMaterial]
            self.selectedBrickNode = nil
        }
    }


}

和GameHelper.swift

class GameHelper {

  static func rad2deg( rad:Float ) -> Float {
    return rad * (Float) (180.0 /  Double.pi)
  }

  static func deg2rad( deg:Float ) -> Float{
   return deg * (Float)(Double.pi / 180)
  }

  static func getPanDirection(velocity: CGPoint) -> String {
    var panDirection:String = ""
    if ( velocity.x > 0 && velocity.x > abs(velocity.y) || velocity.x < 0 && abs(velocity.x) > abs(velocity.y) ){
        panDirection = "horizontal"
    }

    if ( velocity.y < 0 && abs(velocity.y) > abs(velocity.x) || velocity.y > 0 &&  velocity.y  > abs(velocity.x)) {
        panDirection = "vertical"
    }


    return panDirection
  }

}

暫無
暫無

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

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