Sprite Kit Tutorial: How To Drag and Drop Sprites

Learn how to drag and drop sprites (with or without gesture recognizers) in this Sprite Kit tutorial for beginners! By Jean-Pierre Distler.

Leave a rating/review
Save for later
Share

Drag and drop these cute pets with Sprite Kit!

Drag and drop these cute pets with Cocos2D!

Note from Ray: Tutorial Team member Jean-Pierre Distler has ported this tutorial from Cocos2D to Sprite Kit as part of the iOS 7 feast. We hope you enjoy!

In this tutorial, you’re going to learn:

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

To make things fun, you’ll be moving some cute animals around the scene drawn by Ray’s wife Vicki, 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 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 Application\SpriteKit Game, and click “Next“.

Selecting the Sprite Kit Game template

Name the project DragDrop, select iPhone for devices, and click Next. Now save the project to your disc.

Name_Project

Like you did in the Sprite Kit Tutorial for Beginners, you want to make this app landscape only. So select your DragDrop project in the Project Navigator, select your DragDrop target, and make sure only Landscape Left and Landscape Right are checked.

Setting landscape orientation for Sprite Kit

Also the same as you did before, open ViewController.m and replace viewDidLoad with the following:

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];

    // Configure the view.
    SKView * skView = (SKView *)self.view;
    if (!skView.scene) {
      skView.showsFPS = YES;
      skView.showsNodeCount = YES;
      
      // Create and configure the scene.
      SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
      scene.scaleMode = SKSceneScaleModeAspectFill;
      
      // Present the scene.
      [skView presentScene:scene];
    }
}

Next, go ahead and download the images you’ll need for this tutorial. Once you download and unzip the file, drag all of the images into your project. Verify that “Copy items into destination group’s folder (if needed)” is checked, and click Finish.

Add_Assets
Once you’ve added the images to your project, open the Classes group in XCode, and select MyScene.m. Add a class extension above the @implementation and declare two properties you’ll need like the following:

@interface MyScene () 

@property (nonatomic, strong) SKSpriteNode *background;
@property (nonatomic, strong) SKSpriteNode *selectedNode;

@end

You’ll be using these later on to keep track of the background image, the currently selected node/sprite. Now add the following line before the @interface part.

static NSString * const kAnimalNodeName = @"movable";

You will use this String to identify your movable nodes later. Now find the initWithSize: method, and replace it’s content with the following:

- (id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
 
        // 1) Loading the background
        _background = [SKSpriteNode spriteNodeWithImageNamed:@"blue-shooting-stars"];
        [_background setName:@"background"];
        [_background setAnchorPoint:CGPointZero];
        [self addChild:_background];
      
        // 2) Loading the images
        NSArray *imageNames = @[@"bird", @"cat", @"dog", @"turtle"];
        for(int i = 0; i < [imageNames count]; ++i) {
          NSString *imageName = [imageNames objectAtIndex:i];
          SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:imageName];
          [sprite setName:kAnimalNodeName];
       
          float offsetFraction = ((float)(i + 1)) / ([imageNames count] + 1);
          [sprite setPosition:CGPointMake(size.width * offsetFraction, size.height / 2)];
          [_background addChild:sprite];
        }
    }
 
    return self;
}

There's some new stuff in here, so let's go over it step by step.

1) Loading the Background

The first part of the method loads the background image for the scene (blue-shooting-stars.png). Note that it sets the anchor point of the image to the lower left of the image (0, 0).

In Sprite Kit, when you set the position of a node, you are actually setting where the anchor point of the node is. By default, the anchor point is set to the exact center of the node. However, 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.

The method doesn't set the position of the background anywhere, so 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.

2) Loading the Images

The next part of the method loops through the list of images to load. For each image a node is created and placed on the scene. The nodes are distributed along the length of the screen, to have a nice initial layout. It also sets the name to kAnimalNodeName.

Setting the name of a node avoids the need of holding a reference to a node, in case you need it later. After the sprite is created it is added to the _background node.

That's it! Build & run your code, and you should see a couple cute animals sitting there, just begging to be touched!

DragDrop first run

Selecting Sprites based on Touches

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

Replace touchesBegan:withEvent: with the following:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    [self selectNodeForTouch:positionInScene];
}

First you get the touch from the touches set. Then you convert the touches location to the location in a specific node. Here the node is your scene. With this position you call selectNodeForTouch:. This is a new method that you will add now to your scene.

- (void)selectNodeForTouch:(CGPoint)touchLocation {
   //1
   SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];
	
      //2
	if(![_selectedNode isEqual:touchedNode]) {
		[_selectedNode removeAllActions];
		[_selectedNode runAction:[SKAction rotateToAngle:0.0f duration:0.1]];
		
		_selectedNode = touchedNode;
		//3
		if([[touchedNode name] isEqualToString:kAnimalNodeName]) {
			SKAction *sequence = [SKAction sequence:@[[SKAction rotateByAngle:degToRad(-4.0f) duration:0.1],
													  [SKAction rotateByAngle:0.0 duration:0.1],
													  [SKAction rotateByAngle:degToRad(4.0f) duration:0.1]]];
			[_selectedNode runAction:[SKAction repeatActionForever:sequence]];
		}
	}

}

This method is a helper method that is doing three different things.

  1. It asks your scene (self) for the node that is on the position touchLocation.
  2. After it gets the matching node, the method checks if the node is the same as the previous selected node. In this case there is nothing to do and the method returns. If you touched a new node or you haven‘t touched a node yet. The node should run a „wiggle“ animation so you can see which node you selected. But before you start the animation you remove all running actions from the currently selected node and run a rotateToAngle:duration: action on the current node. This makes sure that only one node is animated and that a node goes to it's original, unrotated state when another node is selected.
  3. This if-statement checks if the selected node is one of the animatable nodes. You can check this easily with the name property that you have set in your initWithSize: method. If you selected a animatable node you create a sequence of actions that do the „wiggle“ animation (like the one on the home screen when rearranging/deleting apps) and run thos sequence on the selected node. Normally an action runs for the duration you created it with. To avoid the „wiggle“ animation from stopping after it is finsihed you run it as an action that is repeated forever.

Now add the helper function degToRad to the bottom of your file:

float degToRad(float degree) {
	return degree / 180.0f * M_PI;
}

Sprite Kit uses radians for rotation so this function converts an angle given in degree to radians.

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

Sprites wiggling indicating selection