SCNCamera limit arcball rotation

前端 未结 2 1532
天涯浪人
天涯浪人 2020-12-09 13:08

I have a scene setup with SCNCamera that rotates around an object.

What would be the best way to limit the extents of rotation the camera can achieve around the obj

相关标签:
2条回答
  • 2020-12-09 13:32

    It looks like you're almost there, using just the @Rickster code from the answer you cited.

    The change you could make would be in these lines:

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

    which implicitly allow pitch and yaw to cover the entire sphere. That's where you can do your limiting. For instance, instead of allowing the pitch (eulerAngles.x) to vary from 0 to -π, you could do

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

    to vary smoothly between -π/2 and -π, using full screen vertical scrolling to cover that range. Or you could put hard min/max limits/checks in those two lines to constrain to a particular area of the globe.

    (Edit to address the inertia comment)

    For rotational damping, or inertia, I'd approach it by using the built in SceneKit Physics, and perhaps put the camera on an invisible (no geometry) SCNNode. That camera node becomes a gimbal, similar to the approach taken in this project: An interactive seven-foot globe created entirely in RubyMotion and SceneKit.

    The virtual gimbal then gets an SCNPhysicsBody (you add that, it doesn't come with one by default) with some damping. Or perhaps you put the physics on your central object, and give that object some angularDamping.

    0 讨论(0)
  • 2020-12-09 13:42

    Maybe this could be useful for readers.

        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
            }
    
        }
    
    0 讨论(0)
提交回复
热议问题