2017-10-03 5 views
2

J'écris mon premier projet scenekit et j'essaie d'utiliser un geste Pan pour faire pivoter un objet simple dans ma scène (l'objet est une simple forme de cube en L importée du fichier .dae , le point de pivot est réglé correctement). Je suis allé à travers plusieurs solutions SO et tutoriels et j'ai mis en place du code, mais la rotation ne fonctionne pas correctement. Si je tente à plusieurs reprises de faire pivoter l'objet le long d'un axe, cela fonctionne correctement, mais lorsque j'essaie l'autre sens, au début du panoramique, l'objet se réinitialise à sa position initiale. Parfois, la rotation se dérobe aléatoirement ou saute aussi. Je ne sais pas si je l'ai utilisé la bonne approche à cela, s'il vous plaît conseiller .. Voici mon code:SceneKit - rotation SCNNode avec un mouvement de panoramique basé sur la direction

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 
     } 
    } 


} 

Répondre

4

Après beaucoup de recherches et quelques heures de la paroi se frapper la tête, je finaly trouvé une solution de travail , espérons que cela aide quelqu'un avec un objectif similaire.

Mon code de travail:

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 
     } 
    } 


} 

et 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 
    } 

}