Search code examples
iossprite-kitskcameranode

Math for zooming in and out of a SpriteKit scene


Using these as guides:

SpriteKit pinch to zoom camera

SpriteKit- How to zoom-in and zoom-out of an SKScene?

I've implemented the following functions to zoom in and out of a SpriteKit scene, as well as the ability to incrementally zoom in and out of a scene using two UIButtons:

 var previousCameraScale = CGFloat()
    var count = 0

    @objc func pinchGestureAction(_ sender: UIPinchGestureRecognizer) {
      guard let camera = cadScene.camera else {
        return
      }
        
      if sender.state == .began {
        previousCameraScale = camera.xScale
      }
        
      camera.setScale(previousCameraScale * 1 / sender.scale)
    }

   @IBAction func zoomIn(_ sender: Any) {
        count = count+1
        guard let camera = cadScene.camera else {
          return
        }
        previousCameraScale = camera.xScale
        camera.setScale(previousCameraScale / CGFloat(count))
    }
    
    @IBAction func zoomOut(_ sender: Any) {
        count = count-1

        guard let camera = cadScene.camera else {
          return
        }
        previousCameraScale = camera.xScale
        camera.setScale(previousCameraScale * CGFloat(count))
    }

The pinch to zoom works great; I'm having trouble with the zoom - in and out ones, though...setting the scale to inf and -inf for some reason.

Not the greatest at math; any help appreciated!

EDIT:

Updated logic:

@IBAction func zoomIn(_ sender: Any) {
    count = count+0.1
    
    guard let camera = cadScene.camera else {
        return
    }
    previousCameraScale = camera.xScale - CGFloat(count)
    camera.setScale(previousCameraScale * 1)
    
    print("Previous camera scale \(previousCameraScale)")
}

@IBAction func zoomOut(_ sender: Any) {
    count = count-0.1
    guard let camera = cadScene.camera else {
        return
    }
    previousCameraScale = camera.xScale + CGFloat(count)

    
    if previousCameraScale < 0 {
        return
    }
    
    camera.setScale(previousCameraScale * 1)
    
    print("Previous camera scale \(previousCameraScale)")
}

@objc func pinchGestureAction(_ sender: UIPinchGestureRecognizer) {
    guard let camera = cadScene.camera else {
        return
    }
    
    if sender.state == .began {
        previousCameraScale = camera.xScale
    }
    
    camera.setScale(previousCameraScale * 1 / sender.scale)
    
    print("Previous camera scale \(previousCameraScale)")
    print("Sender scale \(sender.scale)")
    print("Camera scale \(camera.xScale)")
}

Solution

  • Can you try the following? It combines pinch & step zoom

        // MARK: - Zoom
        var scene: SKScene! // Setup this first
        var stepper: UIStepper!
        let zoomStepsInOneDirection: Double = 25
        var cameraScaleAtRest: CGFloat!
    
        func setupCamera() {
            let cameraNode = SKCameraNode()
            scene.addChild(cameraNode)
            scene.camera = cameraNode
            cameraScaleAtRest = cameraNode.xScale
        }
    
        func setupControls() {
            let gr = UIPinchGestureRecognizer(target: self, action: #selector(pinchGestureAction(_:)))
            view.addGestureRecognizer(gr)
    
            let stepper = UIStepper(frame: CGRect(x: 0, y: 50, width: 400, height: 100))
            stepper.minimumValue = -zoomStepsInOneDirection
            stepper.value = 0
            stepper.maximumValue = zoomStepsInOneDirection
            stepper.addTarget(self, action: #selector(stepperPressed(_:)), for: .touchUpInside)
            view.addSubview(stepper)
            self.stepper = stepper
        }
    
        @objc func stepperPressed(_ sender: UIStepper) {
            assert(cameraScaleAtRest != nil)
            assert(scene.camera != nil)
    
            let scaleFactor = exp(sender.value / zoomStepsInOneDirection)
            // the following could also work
    //        let scaleFactor = pow(2, sender.value / zoomStepsInOneDirection)
    
            let newScale = cameraScaleAtRest / scaleFactor
            scene.camera?.setScale(newScale)
        }
    
        @objc func pinchGestureAction(_ sender: UIPinchGestureRecognizer) {
            assert(scene.camera != nil)
    
            guard let camera = scene.camera else {
                return
            }
    
            switch sender.state {
            case .began:
                cameraScaleAtRest = camera.xScale
                stepper.value = 0
            case .ended:
                cameraScaleAtRest = camera.xScale
                stepper.value = 0
            default:
                let newScale = cameraScaleAtRest / sender.scale
                camera.setScale(newScale)
            }
        }