2015-11-28 12 views
8

Ich habe eine Szene Setup mit SCNCamera, die um ein Objekt dreht.SCNCamera Begrenzung Arcball Rotation

Was wäre der beste Weg, um den Umfang der Drehung zu begrenzen, den die Kamera um das Objekt herum erreichen kann?

Beispiel: Wie würde ich die Rotation auf eine einzige Hemisphäre beschränken, anstatt sich um eine ganze Kugel drehen zu können?

Mein erster Versuch war zu sehen, ob es Klemmen für .allowsCameraControl gab. Konnte nichts finden.

Ich versuchte dann C# Einheit: mouse orbit script, kein Glück.

Einige Hinweise darauf, wie man sich nähert oder löst, wären großartig.

Boilerplate Arcball dank 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

Ich glaube nicht, dass die Modul 1 Operation das gewünschte Ergebnis haben wird ('widthRatio% 1'). Der widthRatio wird von -1 bis 1 variieren, aber wenn ein Benutzer das Limit überschreitet, kann sich der Wert von -0,9 auf -1,2 ändern. Der Modulus wird angewendet, was zu einem Wert von 0,2 führt, was einen abrupten Sprung zur anderen Seite der Kugel bewirkt. Eine Möglichkeit wäre, 2 zu addieren, wenn der Wert unter -1 fällt, und 2 zu subtrahieren, wenn das Ende der Rotation über 1 liegt. Bei Verwendung dieser Methode würde der Übergang von der -0,9 Position zu -1,2 zu einem Wert führen von 0,8, was ich glaube, ist das gewünschte Ergebnis. –

Antwort

4

Es sieht aus wie Sie sind fast da, nur mit dem @Rickster Code aus die Antwort, die Sie zitiert.

Die Änderung, die Sie in diesen Zeilen wäre machen könnten:

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

die implizit erlauben Stampfen und Gieren zu decken die gesamte Kugel. Das ist, wo Sie Ihre Begrenzung tun können. Zum Beispiel anstelle der Tonhöhe erlaubt (eulerAngles.x) 0 bis variieren & pgr;, könnten Sie tun

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

reibungslos zwischen -π/2 und -π zu variieren, Vollbild vertikales Scrollen mit zu bedecke diesen Bereich. Oder Sie könnten harte Min/Max-Limits/Checks in diese beiden Zeilen setzen, um auf einen bestimmten Bereich des Globus zu beschränken.

(Bearbeiten des Trägheits Kommentar zu adressieren)

Für Drehdämpfung oder Trägheit, würde ich es Ansatz der in SceneKit Physik gebaut, der vielleicht die Kamera auf einer unsichtbare (ohne Geometrie) setzen SCNNode. Dieser Kameraknoten wird ein Gimbal, ähnlich dem Ansatz in diesem Projekt: An interactive seven-foot globe created entirely in RubyMotion and SceneKit.

Das virtuelle Gimbal erhält dann eine SCNPhysicsBody (Sie fügen hinzu, dass es nicht standardmäßig mit einem kommt) mit einigen damping. Oder vielleicht legen Sie die Physik auf Ihr zentrales Objekt und geben diesem Objekt etwas .

+0

Ausgezeichnet vielen Dank für Ihre Antwort. Nachdem ich Ihre Änderung vorgenommen und einen Ausdruck der Winkel betrachtet hatte, ergab alles einen Sinn und klickte. – Magrafear

+0

Ich habe eine Follow-up-Frage wenn möglich @ hal-mueller? Ich versuche, meiner Bogen-Ball-Rotation eine Trägheit hinzuzufügen. 'lassen sich drehen = SCNAction.rotateByAngle (CGFloat (Float (-M_PI_2) + Float (-M_PI_2) * widthRatio), aroundAxis: SCNVector3Make (0 , 0, 1), Dauer: NSTimeInterval (1))' Ist die nächste, die ich habe, dass es richtig ist. Aber die Kurve dreht sich nicht um den Bogen, wie es scheint. Vielen Dank – Magrafear

4

Vielleicht könnte dies für Leser nützlich sein.

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 
     } 

    }