2015-11-28 25 views
8

Ho una configurazione scena con SCNCamera che ruota attorno a un oggetto.Rotazione arcball limite SCNCamera

Quale sarebbe il modo migliore per limitare i limiti di rotazione che la fotocamera può raggiungere attorno all'oggetto?

Esempio: invece di essere in grado di ruotare attorno a un'intera sfera, come limitarei la rotazione a un singolo emisfero?

Il mio primo tentativo è stato quello di verificare se ci fossero dei morsetti per .allowsCameraControl. Non ho trovato nulla.

Ho quindi provato ad adattare C# Unity: mouse orbit script, senza fortuna.

Alcuni indicatori su come affrontare o risolvere questo sarebbe grandioso.

Boilerplate dell'arco di rotazione grazie alla this answer.

var lastWidthRatio: Float = 0 
var lastHeightRatio: Float = 0 

let camera = SCNCamera() 
let cameraNode = SCNNode() 
let cameraOrbit = SCNNode() 

override func viewDidLoad() { 
    super.viewDidLoad() 

    // create a new scene 
    let scene = SCNScene(named: "art.scnassets/ship.scn")! 

    // create and add a camera to the scene 

    camera.usesOrthographicProjection = true 
    camera.orthographicScale = 9 
    camera.zNear = 0 
    camera.zFar = 100 

    cameraNode.position = SCNVector3(x: 0, y: 0, z: 50) 
    cameraNode.camera = camera 

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

    // retrieve the ship node 
    let ship = scene.rootNode.childNodeWithName("ship", recursively: true)! 

    // retrieve the SCNView 
    let scnView = self.view as! SCNView 

    // set the scene to the view 
    scnView.scene = scene 

    // add a tap gesture recognizer 
    let gesture = UIPanGestureRecognizer(target: self, action: "panDetected:"); 
    scnView.addGestureRecognizer(gesture); 
} 


func panDetected(sender: UIPanGestureRecognizer) { 
    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 

    print(Float(-2 * M_PI) * widthRatio) 
    if (sender.state == .Ended) { 
     lastWidthRatio = widthRatio % 1 
     lastHeightRatio = heightRatio % 1 
    } 
} 
+1

Non penso che l'operazione modulo 1 avrà il risultato desiderato ('widthRatio% 1'). WidthRatio varierà da -1 a 1, ma quando un utente supera il limite, il valore può variare da -0.9 a, ad esempio, -1.2. Il modulo viene applicato, risultando in un valore di 0,2, causando un brusco salto all'altro lato della sfera. Un modo per aggirare sarebbe aggiungere 2 se il valore scende al di sotto di -1 e sottrarre 2 se la fine della rotazione è superiore a 1. Usando tale metodo, passando da -0.9 a -1.2 si otterrebbe un valore di 0,8, che credo sia il risultato desiderato. –

risposta

4

Sembra che ci sei quasi, utilizzando solo il codice @Rickster dal la risposta che hai citato.

La modifica si potrebbe fare sarebbe in queste righe:

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

che implicitamente permettono beccheggio e imbardata per coprire l'intera sfera. È lì che puoi fare il tuo limite. Ad esempio, invece di consentire al passo (eulerAngles.x) per variare da 0 a -π, si potrebbe fare

self.cameraOrbit.eulerAngles.x = Float(-M_PI_2) + Float(-M_PI_2) * heightRatio 

di variare agevolmente tra -π/2 e -π, uso dello schermo intero scorrimento verticale coprire quella gamma. Oppure puoi inserire hard min/max limits/checks in queste due righe per vincolare in una particolare area del globo.

(Modifica per affrontare il commento di inerzia)

per lo smorzamento di rotazione, o l'inerzia, mi avvicino utilizzando il costruito nel SceneKit Fisica, e forse messo la fotocamera su un invisibile (senza geometria) SCNNode. Quel nodo della telecamera diventa un giunto cardanico, simile all'approccio adottato in questo progetto: An interactive seven-foot globe created entirely in RubyMotion and SceneKit.

Il giunto cardanico virtuale ottiene quindi un SCNPhysicsBody (si aggiunge che, non viene fornito con uno di default) con alcuni damping. O forse metti la fisica sul tuo oggetto centrale e dai a quell'oggetto un po 'di angularDamping.

+0

Eccellente grazie mille per la risposta. Dopo aver inserito la tua modifica e aver visto una stampa fuori dagli angoli, tutto ha avuto senso e fatto clic. – Magrafear

+0

Ho una domanda successiva se possibile @ hal-mueller? Sto cercando di aggiungere inerzia alla rotazione della mia palla arcuata. 'far rotate = SCNAction.rotateByAngle (CGFloat (Float (-M_PI_2) + Float (-M_PI_2) * widthRatio), aroundAxis: SCNVector3Make (0 , 0, 1), durata: NSTimeInterval (1))' È il più vicino che ho per essere corretto. Ma la svolta non gira attorno all'arco che sembra. Grazie – Magrafear

4

Forse questo potrebbe essere utile per i lettori.

class GameViewController: UIViewController { 

    var cameraOrbit = SCNNode() 
    let cameraNode = SCNNode() 
    let camera = SCNCamera() 


    //HANDLE PAN CAMERA 
    var lastWidthRatio: Float = 0 
    var lastHeightRatio: Float = 0.2 
    var WidthRatio: Float = 0 
    var HeightRatio: Float = 0.2 
    var fingersNeededToPan = 1 
    var maxWidthRatioRight: Float = 0.2 
    var maxWidthRatioLeft: Float = -0.2 
    var maxHeightRatioXDown: Float = 0.02 
    var maxHeightRatioXUp: Float = 0.4 

    //HANDLE PINCH CAMERA 
    var pinchAttenuation = 20.0 //1.0: very fast ---- 100.0 very slow 
    var lastFingersNumber = 0 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // create a new scene 
     let scene = SCNScene(named: "art.scnassets/ship.scn")! 

     // create and add a light to the scene 
     let lightNode = SCNNode() 
     lightNode.light = SCNLight() 
     lightNode.light!.type = SCNLightTypeOmni 
     lightNode.position = SCNVector3(x: 0, y: 10, z: 10) 
     scene.rootNode.addChildNode(lightNode) 

     // create and add an ambient light to the scene 
     let ambientLightNode = SCNNode() 
     ambientLightNode.light = SCNLight() 
     ambientLightNode.light!.type = SCNLightTypeAmbient 
     ambientLightNode.light!.color = UIColor.darkGrayColor() 
     scene.rootNode.addChildNode(ambientLightNode) 

    //Create a camera like Rickster said 
     camera.usesOrthographicProjection = true 
     camera.orthographicScale = 9 
     camera.zNear = 1 
     camera.zFar = 100 

     cameraNode.position = SCNVector3(x: 0, y: 0, z: 50) 
     cameraNode.camera = camera 
     cameraOrbit = SCNNode() 
     cameraOrbit.addChildNode(cameraNode) 
     scene.rootNode.addChildNode(cameraOrbit) 

     //initial camera setup 
     self.cameraOrbit.eulerAngles.y = Float(-2 * M_PI) * lastWidthRatio 
     self.cameraOrbit.eulerAngles.x = Float(-M_PI) * lastHeightRatio 

     // retrieve the SCNView 
     let scnView = self.view as! SCNView 

     // set the scene to the view 
     scnView.scene = scene 

     //allows the user to manipulate the camera 
     scnView.allowsCameraControl = false //not needed 

     // add a tap gesture recognizer 
     let panGesture = UIPanGestureRecognizer(target: self, action: "handlePan:") 
     scnView.addGestureRecognizer(panGesture) 

     // add a pinch gesture recognizer 
     let pinchGesture = UIPinchGestureRecognizer(target: self, action: "handlePinch:") 
     scnView.addGestureRecognizer(pinchGesture) 
    } 

    func handlePan(gestureRecognize: UIPanGestureRecognizer) { 

     let numberOfTouches = gestureRecognize.numberOfTouches() 

     let translation = gestureRecognize.translationInView(gestureRecognize.view!) 

     if (numberOfTouches==fingersNeededToPan) { 

      widthRatio = Float(translation.x)/Float(gestureRecognize.view!.frame.size.width) + lastWidthRatio 
      heightRatio = Float(translation.y)/Float(gestureRecognize.view!.frame.size.height) + lastHeightRatio 

      // HEIGHT constraints 
      if (heightRatio >= maxHeightRatioXUp) { 
       heightRatio = maxHeightRatioXUp 
      } 
      if (heightRatio <= maxHeightRatioXDown) { 
       heightRatio = maxHeightRatioXDown 
      } 


      // WIDTH constraints 
      if(widthRatio >= maxWidthRatioRight) { 
       widthRatio = maxWidthRatioRight 
      } 
      if(widthRatio <= maxWidthRatioLeft) { 
       widthRatio = maxWidthRatioLeft 
      } 

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

      print("Height: \(round(heightRatio*100))") 
      print("Width: \(round(widthRatio*100))") 


      //for final check on fingers number 
      lastFingersNumber = fingersNeededToPan 
     } 

     lastFingersNumber = (numberOfTouches>0 ? numberOfTouches : lastFingersNumber) 

     if (gestureRecognize.state == .Ended && lastFingersNumber==fingersNeededToPan) { 
      lastWidthRatio = widthRatio 
      lastHeightRatio = heightRatio 
      print("Pan with \(lastFingersNumber) finger\(lastFingersNumber>1 ? "s" : "")") 
     } 
    } 

    func handlePinch(gestureRecognize: UIPinchGestureRecognizer) { 
     let pinchVelocity = Double.init(gestureRecognize.velocity) 
     //print("PinchVelocity \(pinchVelocity)") 

     camera.orthographicScale -= (pinchVelocity/pinchAttenuation) 

     if camera.orthographicScale <= 0.5 { 
      camera.orthographicScale = 0.5 
     } 

     if camera.orthographicScale >= 10.0 { 
      camera.orthographicScale = 10.0 
     } 

    }