Sprite Kit Tutorial: Drag and Drop Sprites

Sprite Kit Tutorial: Drag and Drop Sprites
Drag and drop these cute pets with Sprite Kit!

Drag and drop these cute pets with Sprite Kit!

Update note: Team member Riccardo D’Antoni has updated this tutorial originally by Ray Wenderlich (Cocos-2D version) and update by Jean-Pierre Distler (Objective-C version). It’s now fully up-to-date for iOS 8 and Swift!

Handling touches and swipes is right at the core of most apps – after all, it’s how your users interact with the user interface!

In Sprite Kit, there’s no UIButton equivalent to handle simple taps, and you need to implement touch handling yourself with callbacks or with gesture recognizers.

Not to worry though; in this tutorial, you’re going to learn all about handling touches and drag-and-drop:

  • The basics of dragging and dropping sprites with touches
  • How to scroll the view itself via touches
  • How to use gesture recognizers with Sprite Kit for even more cool effects!

To make things fun, you’ll be moving some cute animals from a Game Art Guppy art pack, on a background made by gwebstock.

This tutorial assumes you at least know the basics of Sprite Kit. If you are completely new to Sprite Kit, be sure to check out our Sprite Kit Swift Tutorial for Beginners first.

So without further ado, drag your fingers over to the keyboard and let’s get started!

Getting Started

Before you implement the touch handling, first you’ll create a basic Sprite Kit scene displaying the sprites and artwork.

Open up Xcode, go to File\New Project, choose the iOS\Application\Game template, and click Next.


Name the project DragDropSwift, select Swift as the Language, SpriteKit as the Game Technology, and iPhone for Devices. Click Next to continue, and select a location to save the project.


For layout simplicity, you’ll limit the app to landscape only. Select your DragDropSwift project in the Project Navigator, select your DragDropSwift target, and make sure only Landscape Left and Landscape Right are checked.


Open GameViewController.swift and delete the SKNode extension as well as viewDidLoad(). You won’t be using the scene from the scene editor, but will set up things from code instead.

Add the following method to GameViewController:

override func viewWillLayoutSubviews() { 
  // Configure the view.
  let skView = self.view as! SKView
  skView.showsFPS = true
  skView.showsNodeCount = true
  /* Sprite Kit applies additional optimizations to improve rendering performance */
  skView.ignoresSiblingOrder = true
  let scene = GameScene(size: skView.frame.size)
  /* Set the scale mode to scale to fit the window */
  scene.scaleMode = .AspectFill

This is the standard Sprite Kit boilerplate to display the starter scene.

Open Images.xcassets. You can delete the starter Spaceship image since you’ll be dealing with friendly animals and not spaceships for this tutorial!

Next, download the resources you’ll need for this tutorial. Once you download and unzip the file, drag all of the images into the asset catalog.


Once you’ve added the images to your project, open GameScene.swift and replace the entire contents of the file with the following:

import SpriteKit
private let kAnimalNodeName = "movable"
class GameScene: SKScene {
  let background = SKSpriteNode(imageNamed: "blue-shooting-stars")
  var selectedNode = SKSpriteNode()

You don’t need the starter “Hello World” scene; instead, you have a constant that you’ll use later to track the currently moving sprite, and two properties to keep track of the background and image sprites.

Next, add the following initializers to the class:

required init?(coder aDecoder: NSCoder) {
  fatalError("init(coder:) has not been implemented")
override init(size: CGSize) {
  super.init(size: size)
  // 1
  self.background.name = "background"
  self.background.anchorPoint = CGPointZero
  // 2
  // 3
  let imageNames = ["bird", "cat", "dog", "turtle"]
  for i in 0..<imageNames.count {
    let imageName = imageNames[i]
    let sprite = SKSpriteNode(imageNamed: imageName)
    sprite.name = kAnimalNodeName
    let offsetFraction = (CGFloat(i) + 1.0)/(CGFloat(imageNames.count) + 1.0)
    sprite.position = CGPoint(x: size.width * offsetFraction, y: size.height / 2)

The init(coder:) initializer is required by the compiler, but you won’t actually use it. The real logic is in the overridden init(size:); let’s go over it step by step.

  1. First, you set up the background for the scene by giving a name that will be used in the app logic later on. Next, you set the anchor poin of the image to the lower left of the image, at (0, 0). The anchor point in Sprite Kit is at the center of the node by default, which means you’re usually setting the center point when you set a node’s position. By setting the anchor point to the lower left corner, when you set the position of the node, you are now setting where the lower left corner is.
  2. You’re not setting the position of the background but just adding it to the node hierarchy. That means the position of the background defaults to (0,0). Hence, the lower left corner of the image is located at (0,0), and so the image (which is about 800 points long) extends off screen to the right.
  3. The next part of the method loops through the list of images to load. For each image, you create a node and place it on the scene. The nodes are distributed along the length of the screen, to have a nice initial layout. Setting the name of a node avoids the need of holding a reference to a node, in case you need it later.

That’s it! Build and run your code, and you should see some cute animals sitting there, just begging to be touched!


Selecting Sprites based on Touches

Now you’ll work on the code to determine which sprite is selected based on the user’s current touch.

Add the implementation of touchesBegan(_:withEvent:) to start handling taps:

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
  let touch = touches.anyObject() as UITouch
  let positionInScene = touch.locationInNode(self)

First you get the touch from the set of touches, and convert the touch coordinates to the coordinate system of the scene. With this position, you call selectNodeForTouch(_:), which you’ll implement next to select one of the animals.

Add the following helper methods to the class next:

func degToRad(degree: Double) -> CGFloat {
  return CGFloat(Double(degree) / 180.0 * M_PI)
func selectNodeForTouch(touchLocation: CGPoint) {
  // 1
  let touchedNode = self.nodeAtPoint(touchLocation)
  if touchedNode is SKSpriteNode {
    // 2
    if !selectedNode.isEqual(touchedNode) {
      selectedNode.runAction(SKAction.rotateToAngle(0.0, duration: 0.1))
      selectedNode = touchedNode as! SKSpriteNode
      // 3
      if touchedNode.name! == kAnimalNodeName {
        let sequence = SKAction.sequence([SKAction.rotateByAngle(degToRad(-4.0), duration: 0.1),
          SKAction.rotateByAngle(0.0, duration: 0.1),
          SKAction.rotateByAngle(degToRad(4.0), duration: 0.1)])

Sprite Kit uses radians for rotation so the first method angle given in degree to radians.

selectNodeForTouch(_:) selects one of the animal sprites based on location, in three steps:

  1. First, it finds the node at the touchLocation.
  2. If the node found is a SKSpriteNode instance, you first check if the node is the same as the previously selected node. In this case there is nothing to do and the method returns early. If this is a freshly selected node, then you first reset it by removing all actions on the node and then setting it back to its original, unrotated state.
  3. This if-statement checks if the selected node is one of the animatable animal nodes by checking the name property that you set. If so, you create a sequence of actions for a “wiggle” animation, like the one on the home screen when rearranging/deleting apps, and then run this sequence on the selected node. To keep the level of excitement high, you run it as an action that is repeated forever.

Build and run your code, and you should now be able to tap on the animals. When you tap them they should wiggle in a particularly cute way to show that they are selected!

Sprites wiggling indicating selection

Moving Sprites and the Layer based on Touches

Time to make these animals move! The basic idea is you’ll implement touchesMoved(_:withEvent:), and figure out how much the touch has moved since last time. If an animal is selected, it will move the animal by that amount. If an animal is not selected, it will move the entire layer instead, so that the user can scroll the layer from left to right.

To understand how you can scroll a node in Sprite Kit, start by taking a look at the image below:

Scrolling layers with Cocos2D

As you can see, you’ve set up the background so the anchor point (the lower left) is at (0, 0), and the rest extends off to the right. The black area indicates the current visible area (the size of the window).

If you want to scroll the image 100 points to the right, you can do that by moving the entire Sprite Kit node 100 points to the left, as you can see in the second image.

You also want to make sure you don’t scroll too far. For example, you shouldn’t be able to move the layer to the right, since there would be a blank space where the background doesn’t cover.

Now that you’re armed with this background information, let’s see what it looks like in code! Still in GameScene.swift, add the following new methods to the class:

func boundLayerPos(aNewPosition: CGPoint) -> CGPoint {
  let winSize = self.size
  var retval = aNewPosition
  retval.x = CGFloat(min(retval.x, 0))
  retval.x = CGFloat(max(retval.x, -(background.size.width) + winSize.width))
  retval.y = self.position.y
  return retval
func panForTranslation(translation: CGPoint) {
  let position = selectedNode.position
  if selectedNode.name! == kAnimalNodeName {
    selectedNode.position = CGPoint(x: position.x + translation.x, y: position.y + translation.y)
  } else {
    let aNewPosition = CGPoint(x: position.x + translation.x, y: position.y + translation.y)
    background.position = self.boundLayerPos(aNewPosition)

The first method boundLayerPos(_:) is used for making sure you don’t scroll the layer beyond the bounds of the background image. You pass in the coordinates of where you’d like to move the layer, and it returns a possibly modified point to make sure you don’t scroll too far.

The next method panForTranslation(_:) first checks if selectedNode is an animal node and sets the position based on a passed-in translation. If the selected node is the background layer it sets calls boundLayerPos(_:) in addition to setting the position to make sure that you cannot scroll too far to the left or right.

Now you can implement touchesMoved(_:withEvent:) to start handling pans:

override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
  let touch = touches.anyObject() as UITouch
  let positionInScene = touch.locationInNode(self)
  let previousPosition = touch.previousLocationInNode(self)
  let translation = CGPoint(x: positionInScene.x - previousPosition.x, y: positionInScene.y - previousPosition.y)

Like you did in touchesBegan(_:withEvent:) you first get the touch and convert its position to the position in your scene. To calculate the translation, or how far you’ve dragged your finger on the screen, you need to start with the previous location of the touch.

With the current and previous location you create the translation by subtracting the current location from the last one. Finally you call panForTransaltion(_:) with the calculated translation to handle the scrolling.

Give it a shot – build and run your code, and you should now be able to move the sprites and the layer by dragging!

Dragging sprites with touch with Cocos2D

How to Use Gesture Recognizers with Sprite Kit

There’s another way to accomplish what you just did with Sprite Kit touch handling – use gesture recognizers instead!

Gesture recognizers are a great way to detect different gestures like taps, double taps, swipes or pans. Instead of implementing the touch-handling methods yourself and try to distinguish between taps, double taps, swipes, pans and pinches, you simply create a gesture recognizer object for what you want to detect, and add it to the view.

They are extremely easy to use, and you can use them with Sprite Kit with no troubles. Let’s see how that works.

First, comment out the touch handling methods, touchesBegan(_:withEvent:) and touchesMoved(_:withEvent:) since you will be using a different method now.

Next, add the following method to the class:

override func didMoveToView(view: SKView) {
  let gestureRecognizer = UIPanGestureRecognizer(target: self, action: Selector("handlePanFrom:"))

This method gets called when the scene is first presented. Here, you create a pan gesture recognizer and initialize it with your scene as the target and handlePanFrom: as the callback method. Finally, you add the gesture recognizer to your scene’s presenting view.

Note: You may ask yourself why recognizer is added here and not in the initializer. SKScene has a view property that holds the SKView that is presenting the scene, but unfortunately this property is first set when the scene is presented on the screen. That means the property is nil while the object is initializing. didMoveToView(_:) is like viewDidAppear(_:) in UIKit views, and gets called after your scene is presented.

Next, add the following to the class:

func handlePanFrom(recognizer: UIPanGestureRecognizer) {
  if recognizer.state == .Began {
    var touchLocation = recognizer.locationInView(recognizer.view)
    touchLocation = self.convertPointFromView(touchLocation)
  } else if recognizer.state == .Changed {
    var translation = recognizer.translationInView(recognizer.view!)
    translation = CGPoint(x: translation.x, y: -translation.y)
    recognizer.setTranslation(CGPointZero, inView: recognizer.view)
  } else if recognizer.state == .Ended {
    if selectedNode.name != kAnimalNodeName {
      let scrollDuration = 0.2
      let velocity = recognizer.velocityInView(recognizer.view)
      let pos = selectedNode.position
      // This just multiplies your velocity with the scroll duration.
      let p = CGPoint(x: velocity.x * CGFloat(scrollDuration), y: velocity.y * CGFloat(scrollDuration))
      var newPos = CGPoint(x: pos.x + p.x, y: pos.y + p.y)
      newPos = self.boundLayerPos(newPos)
      let moveTo = SKAction.moveTo(newPos, duration: scrollDuration)
      moveTo.timingMode = .EaseOut

This callback gets called when the pan gesture begins, changes (i.e the user continues to drag), and ends. The method switches on each case, and does the appropriate action.

When the gesture begins, it converts the coordinates to node coordinates (note it has to do it the long way because there’s no shortcut method), and calls the selectNodeForTouch(_:) helper you wrote earlier to select a node.

When the gesture changes, it needs to figure out the amount the gesture moved. One of the nice things about gesture recognizers it actually stores for you the cumulative translation for the gesture so far! However, you have to reverse the y coordinate to take into effect the difference between UIKit coordinates and Sprite Kit coordinates.

After panning for the translation, it resets the translation on the recognizer to zero, because otherwise the translation is cumulative, and you just want the difference each time.

When the gesture ends, there’s some new and interesting code in here! Another cool thing a UIPanGestureRecognizer gives you is the velocity of the pan movement. You can use this to animate the background node to slide a bit, so the user can flick quickly to get the background to slide a bit, like you’re used to seeing in scroll views. Based on the velocity, you run a move action with easing to make it feel more natural.

Build and run the project, and you should now be able to slide and move around, all with gesture recognizers!

Moving sprites with UIGestureRecognizers with Cocos2D

Remember to try the velocity-powered movement for the background – try a small flick-like swipe from left to right on the background and you’ll see it continue to scroll a bit after you lift your finger.

Where To Go From Here?

You can download the final project with all of the code from this tutorial.

At this point you should know how to move nodes using touches in your Sprite Kit apps and games, and should know the basics of using gesture recognizers with Sprite Kit.

From here, you could try extending this project with other gesture recognizers, such as perhaps pinch or rotate gesture recognizers. Maybe you can make the cat grow!

If you want to learn more about Sprite Kit, you should check out our book iOS Games by Tutorials. We’ll teach you everything you need to know – from physics, to tile maps, to particle systems, and even making your own level editor.

If you have any questions or comments about this tutorial, please join the discussion below!

The post Sprite Kit Tutorial: Drag and Drop Sprites appeared first on Ray Wenderlich.



Write a comment