2016-05-16 5 views
2

Ich verwende SceneKit und erlaube Benutzern, das, was sie sehen, in einer Kugel zu drehen, indem sie eine Schwenkgeste verwenden. Das funktioniert gut - außer, ich möchte die Kugel leicht in die Richtung der Schwenkbewegung drehen lassen, um ein bisschen reaktiver zu wirken.SceneKit - Hinzufügen von Drift zu einer Kugel, die mit einer Schwenkbewegung gedreht wurde

Hier ist mein Setup für die Szene:

// Create scene 
    let scene = SCNScene() 

    sceneView.scene = scene 

    let panRecognizer = UIPanGestureRecognizer(target: self, 
               action: #selector(ViewController.handlePanGesture(_:))) 

    sceneView.addGestureRecognizer(panRecognizer) 

    //Create sphere 
    let sphere = SCNSphere(radius: 50.0) 

    // sphere setup 

    sphereNode = SCNNode(geometry: sphere) 

    sphereNode!.position = SCNVector3Make(0,0,0) 
    scene.rootNode.addChildNode(sphereNode!) 

    // Create camera 
    let camera = SCNCamera() 

    // camera setup 

    cameraNode = SCNNode() 

    cameraNode!.camera = camera 
    cameraNode!.position = SCNVector3Make(0, 0, 0) 

    cameraOrbit = SCNNode() 

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

    let lookAtConstraint = SCNLookAtConstraint(target: sphereNode!) 
    lookAtConstraint.gimbalLockEnabled = true 
    cameraNode!.constraints = [lookAtConstraint] 

    sceneView.pointOfView = cameraNode 

Und hier ist, wie die pan Geste zur Zeit (mit freundlicher Genehmigung von SCNCamera limit arcball rotation) behandelt wird:

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 

    if (sender.state == .Ended) { 
     lastWidthRatio = widthRatio 
     lastHeightRatio = heightRatio 
    } 

Ich bin mir nicht sicher, wo in beginnen Hinzufügen einer leichten fortgesetzten Drehbewegung. Vielleicht eine physicsBody hinzufügen und Kraft anwenden? Vielleicht animiert die Änderung in eulerAngles?

Antwort

4

Die Art und Weise, wie ich zuvor solche Dinge gehandhabt habe, besteht darin, den Gestenhandler in Abschnitte für den Gestenstatus zu unterteilen, der beginnt, sich ändert und endet. Wenn die Rotation für Sie funktioniert, müssen Sie lediglich den Code am Ende Ihrer if (sender.state == .Ended)-Anweisung hinzufügen.

Nach lastHeightRatio = heightRatio Sie die Geschwindigkeit der Pfanne in der Ansicht sender.velocityInView(sender.view!) und dann die horizontale Komponente wie zuvor hinzufügen finden Verwendung finden können, dann ein SCNAction den Knoten mit einem EaseOut Timing-Modus zu drehen.

Sie benötigen wahrscheinlich einen Multiplikator, um die Geschwindigkeit in der Ansicht (gemessen in Punkten) in den Winkel zu übersetzen, den Sie durchdrehen möchten (gemessen im Bogenmaß). Eine Geschwindigkeit von 10 Punkten, die relativ klein ist, würde zu einer sehr schnellen Rotation führen.

Sie müssen auch alle Aktionen vom Knoten entfernen, wenn die Geste erneut beginnt, wenn Sie eine Berührung wünschen, um die restliche Rotation zu stoppen.

würde Einige sampel Code sein:

if (sender.state == .Ended) { 
    lastWidthRatio = widthRatio 
    lastHeightRatio = heightRatio 

    // Find velocity 
    let velocity = sender.velocityInView(sender.view!) 

    // Create Action for whichever axis is the correct one to rotate about. 
    // The 0.00001 is just a factor to provide the rotation speed to pan 
    // speed ratio you'd like. Play with this number and the duration to get the result you want 
    let rotate = SCNAction.rotateByX(velocity.x * 0.00001, y: 0, z: 0, duration: 2.0) 

    // Run the action on the camera orbit node (if I understand your setup correctly) 
    cameraOrbit.runAction(rotate) 
} 

Dies sollte ausreichen, um Sie zu erhalten begonnen.

+0

Ich landete etwas ähnliches - danke! Mein Problem ist nun, dass der 'lastRatio'-Wert jetzt deaktiviert ist, weil die letzte Ruheposition der Kugel sich weiter gedreht hat als das letzte Mal, als dieser Wert gesetzt wurde. Wenn der Benutzer also die nächste Schwenkgeste ausführt, "springt" die Kamera zu der Position, bevor die Drift gestartet wurde, während die "eulerAngles" zurückgesetzt werden. Wenn Sie irgendwelche Gedanken dazu haben, bin ich ganz Ohr. –

+0

Hmm, versuchen Sie vielleicht, einen 'closer'-Handler zu' runAction 'hinzuzufügen, der die lastRatio-Werte erneut aktualisiert, wenn die Aktion abgeschlossen ist? –

+0

@AndyHeard Ich habe das versucht, aber es funktioniert nicht und ich verstehe wirklich nicht warum. Hier ist mein Code-Schnipsel '' 'lastWidthRatio + = (((-velocity.x * 0.001) * 2)/sender.view.frame.size.width);' '' –