Moving Camera in SpriteKit Swift
Asked Answered
G

4

8

I am creating a game in sprite kit using swift, and I am trying to be able to move the SKScene around with a finger because not all of the nodes fit within the scene. I have already created world, overlay, and camera nodes with this code.

        override func didMoveToView(view: SKView) {
    world = self.childNodeWithName("world")!

    if !isCreated {
        isCreated = true

        // Camera setup
        self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        self.world = SKNode()
        self.world.name = "world"
        addChild(self.world)

        self.cam = SKNode()
        self.cam.name = "camera"
        self.world.addChild(self.cam)

        // UI setup
        self.overlay = SKNode()
        self.overlay.zPosition = 10
        self.overlay.name = "overlay"
        addChild(self.overlay)
    }

I would like to be able to move the camera around by using a pan gesture with a single finger. How would I do this? Any help would be appreciated.

Griseous answered 10/8, 2016 at 6:28 Comment(0)
G
6

As an alternative to @Kris's solution (which is based in UIKit), you can also monitor touches in Sprite Kit with your SKScene subclass. I wrote a small sample which should point you in the right direction.

class YourSceneSubclass : SKScene
{  
  override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    guard let touch = touches.first else {
      return
    }

    let location = touch.locationInNode(self)
    let previousLocation = touch.previousLocationInNode(self)

    camera?.position.x += location.x - previousLocation.x
    camera?.position.y += location.y - previousLocation.y
  }
}

I didn't run this, just wrote it in a playground. Also note that if you want to handle other taps/gestures as well you will have to write additional code making sure the recognition works well for all your intended scenarios.

Gibbs answered 10/8, 2016 at 9:54 Comment(5)
Kris's solution is better, you can't handle multi touch in your scenario, plus you can use other features in the pan to create smoother animations and transitionsSinner
@Sinner Totally agree that UIGestureRecognizers are more robust and flexible than "manually" intercepting the touches by overriding the touches methods. Nonetheless, I wanted to demonstrate this way because it is so stupidly simple to implement and it might add to OP's view on Sprite Kit.Gibbs
oh I completely understand, my comment was just for others who view this in the future, they should try to implement Kris's method first, that is allSinner
@Gibbs Can you explain what you mean by "more robust and flexible" please?Schleswig
@Sinner What are the other features in the pan (I assume gesture recogniser) that you can use to create smooth animations?Schleswig
D
11

If anybody needs it, here is an all-in-one solution:

class GameScene: SKScene {

  var previousCameraPoint = CGPoint.zero

  override func didMove(to view: SKView) {
    let panGesture = UIPanGestureRecognizer()
    panGesture.addTarget(self, action: #selector(panGestureAction(_:)))
    view?.addGestureRecognizer(panGesture)
  }

  @objc func panGestureAction(_ sender: UIPanGestureRecognizer) {
    // The camera has a weak reference, so test it
    guard let camera = self.camera else {
      return
    }
    // If the movement just began, save the first camera position
    if sender.state == .began {
      previousCameraPoint = camera.position
    }
    // Perform the translation
    let translation = sender.translation(in: self.view)
    let newPosition = CGPoint(
      x: previousCameraPoint.x + translation.x * -1,
      y: previousCameraPoint.y + translation.y
    )
    camera.position = newPosition
  }

}
Dameron answered 31/5, 2018 at 11:15 Comment(1)
This is the best answerFull
G
6

As an alternative to @Kris's solution (which is based in UIKit), you can also monitor touches in Sprite Kit with your SKScene subclass. I wrote a small sample which should point you in the right direction.

class YourSceneSubclass : SKScene
{  
  override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    guard let touch = touches.first else {
      return
    }

    let location = touch.locationInNode(self)
    let previousLocation = touch.previousLocationInNode(self)

    camera?.position.x += location.x - previousLocation.x
    camera?.position.y += location.y - previousLocation.y
  }
}

I didn't run this, just wrote it in a playground. Also note that if you want to handle other taps/gestures as well you will have to write additional code making sure the recognition works well for all your intended scenarios.

Gibbs answered 10/8, 2016 at 9:54 Comment(5)
Kris's solution is better, you can't handle multi touch in your scenario, plus you can use other features in the pan to create smoother animations and transitionsSinner
@Sinner Totally agree that UIGestureRecognizers are more robust and flexible than "manually" intercepting the touches by overriding the touches methods. Nonetheless, I wanted to demonstrate this way because it is so stupidly simple to implement and it might add to OP's view on Sprite Kit.Gibbs
oh I completely understand, my comment was just for others who view this in the future, they should try to implement Kris's method first, that is allSinner
@Gibbs Can you explain what you mean by "more robust and flexible" please?Schleswig
@Sinner What are the other features in the pan (I assume gesture recogniser) that you can use to create smooth animations?Schleswig
S
3

First you have to create UIPanGestureRecognizer. Then add it to the view of your scene. In your action method of the gesture recognizer you could use translationInView:. Based on that you can modify position of the camera.

Also while creating the gesture recognizer you can configure number of touches by maximumNumberOfTouches: and minimumNumberOfTouches:.

Sweeping answered 10/8, 2016 at 8:3 Comment(0)
E
0

If you find that your horizontal movement is opposite than your desired expectation, try the following (Swift 3):

let panGesture = UIPanGestureRecognizer()    
var previousPoint = CGPoint.zero

func  addPanGesture()
    {
        self.panGesture.addTarget(self, action:#selector(BaseScene.pannedView(_:) ))
        self.panGesture.delegate = self

        self.view?.addGestureRecognizer(self.panGesture)
    }
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool
{
    self.previousPoint = self.camera.position

    return true
}


func pannedView(_ sender:UIPanGestureRecognizer)
{
    let transPoint = sender.translation(in: self.view)

    let newPosition = self.previousPoint + (CGPoint(x: transPoint.x * -1.0, y: transPoint.y * 1.0))

    self.camera.position = newPosition
}
Evanne answered 18/6, 2017 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.