Tutorial level: Easy

Today we are going to get a handle on Sprites in iOS.  For this tutorial, we will be using XCode 6.1.1 and writing our app in the Swift programming language.  Let’s get started!

First, open up a new XCode iOS project.  I named mine SpriteSpinAndGo.  After you’ve finished creating the project you should come to a screen that looks like this:

sng1

 

 

On the left side of XCode, you will see a navigation of your project files.  Select GameScene.swift.  This is where we will do all of our coding for this project.

 

Initially, you should see something like this:

import SpriteKit

class GameScene: SKScene {
    override func didMoveToView(view: SKView) {
        /* Setup your scene here */
        let myLabel = SKLabelNode(fontNamed:"Chalkduster")
        myLabel.text = "Hello, World!";
        myLabel.fontSize = 65;
        myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
        
        self.addChild(myLabel)
    }
    
    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        /* Called when a touch begins */
        
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)
            
            let sprite = SKSpriteNode(imageNamed:"Spaceship")
            
            sprite.xScale = 0.5
            sprite.yScale = 0.5
            sprite.position = location
            
            let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
            
            sprite.runAction(SKAction.repeatActionForever(action))
            
            self.addChild(sprite)
        }
    }
   
    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }
}

 
Let’s see what this code does by pressing command + R. This will build and run our application in XCode’s iOS simulator. Click on the app screen and you should see a spaceship appear and spin. The more you click, the more spaceships.
 
SpriteApp
 
Now we’re ready!

Recall that the intent of this tutorial is to create a single sprite that will rotate and move to any touches on the screen. Let’s begin by shelling out what we want our app to do. Directly below the touchesBegan function, let’s create two empty functions:

    func createSpriteAt(location: CGPoint) {

    }
    
    func spinSpriteAndGoTo(sprite: SKSpriteNode, location: CGPoint) {

    }

The first function takes a location and will create our sprite. Inside the touchesBegan function, you will see the implementation to create a sprite. Let’s move it to our first function like so.

    func createSpriteAt(location: CGPoint) {
        let sprite = SKSpriteNode(imageNamed:"Spaceship")
        
        sprite.xScale = 0.5
        sprite.yScale = 0.5
        sprite.position = location
        
        let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
        
        sprite.runAction(SKAction.repeatActionForever(action))
        
        self.addChild(sprite)
    }

We want this code to run only once, on the first touch. To do that, let’s add a boolean to the top of our page directly after we declare our GameScene instance.

    var spriteExists = false;

We initially set spriteExists to false because when we launch our app, no spaceships exist. Every time we tap the screen, a new one will appear. However, we only want one spaceship to appear. Let’s control the number of spaceships added by including an if-else statement that checks if a spaceship has been added. If a spaceship has not been added, we’ll call our new function createSpriteAt(location) and set spriteExists to true.

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        /* Called when a touch begins */
        
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)
                if spriteExists {
                    //spinSpriteAndGoTo(sprite, location: location)
                } else {
                    createSpriteAt(location)
                    spriteExists = true
                }
            }
        }
    }

Great! If you run the app, you will see that only one spaceship displays. Now let’s add a spin and move action to our spaceship sprite.

Uncomment the spinSpriteAndGoTo function. You should see XCode giving you an error because you have not defined what ‘sprite’ is. We can easily identify our sprite by giving it a name. Take a look at our createSpriteAt function. Right after we declare our sprite, let’s give it a name of “spaceship” by adding the following line of code.

sprite.name = "spaceship"

Now we can identify this node anywhere in our application. Let’s finish defining our sprite in the touchesBegan function so that we can successfully call the spinSpriteAndGoTo function. Your complete touchesBegan function should look like the following.

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)
            if spriteExists {
                let sprite = self.childNodeWithName("spaceship") as SKSpriteNode
                spinSpriteAndGoTo(sprite, location: location)
            } else {
                createSpriteAt(location)
                spriteExists = true
            }
        }
    }

Almost there. All that’s left is to complete the spinSpriteAndGoTo function. We’ll use the SKAction object to rotate and move our sprite.

Let’s define our actions.

        let spinAction = SKAction.rotateToAngle(CGFloat?, duration:0.3)
        let moveAction = SKAction.moveTo(location, duration: 0.5)
        let actions = SKAction.sequence([spinAction, moveAction])

We can assign these actions to our spriteNode like so.

        sprite.runAction(actions)

All that’s missing is the angle of rotation. If you’re good at math, this is simple. If you’re anything like me and you have nightmares of your highschool geometry class, don’t worry. You’re going to be OK. The beauty of the web is that it’s full of answers to questions that you haven’t begun to ask. Lucky for you, I did just that.

A simple google search of “Rotation animation in the touched direction” might land you here: http://stackoverflow.com/questions/11431471/uiimageview-rotation-animation-in-the-touched-direction

Finally, with a bit of context clues we can calculate our angle of rotation using the equation

let angle = atan2(deltaY, deltaX) - CGFloat(M_PI_2)

After defining deltaY, deltaX, and plugging in our angle, our final spinSpriteAndGoTo function should look like this.

    func spinSpriteAndGoTo(sprite: SKSpriteNode, location: CGPoint) {
        let deltaX = location.x - sprite.position.x
        let deltaY = location.y - sprite.position.y
        let angle = atan2(deltaY, deltaX) - CGFloat(M_PI_2)
        
        let spinAction = SKAction.rotateToAngle(angle, duration:0.3)
        let moveAction = SKAction.moveTo(location, duration: 0.5)
        let actions = SKAction.sequence([spinAction, moveAction])
        
        sprite.runAction(actions)
    }

I hope you found this tutorial both easy to understand and insightful. If you have any questions or feedback, please leave a comment below. The full source code can be seen below as well. Stay tuned for more tutorials in the future!

//
//  GameScene.swift
//  SpriteSpinAndGo
//
//  Created by Daniel Peterson on 2/10/15.
//  Copyright (c) 2015 DanielPeterson. All rights reserved.
//

import SpriteKit

class GameScene: SKScene {
    var spriteExists = false;
    
    override func didMoveToView(view: SKView) {
        /* Setup your scene here */
        let myLabel = SKLabelNode(fontNamed:"Chalkduster")
        myLabel.text = "Spin and Go!";
        myLabel.fontSize = 25;
        myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
        
        self.addChild(myLabel)
    }
    
    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)
            if spriteExists {
                let sprite = self.childNodeWithName("spaceship") as SKSpriteNode
                spinSpriteAndGoTo(sprite, location: location)
            } else {
                createSpriteAt(location)
                spriteExists = true
            }
        }
    }
    
    func createSpriteAt(location: CGPoint) {
        let sprite = SKSpriteNode(imageNamed:"Spaceship")
        sprite.name = "spaceship"
        
        sprite.xScale = 0.5
        sprite.yScale = 0.5
        sprite.position = location
        
        self.addChild(sprite)
    }
    
    func spinSpriteAndGoTo(sprite: SKSpriteNode, location: CGPoint) {
        let deltaX = location.x - sprite.position.x
        let deltaY = location.y - sprite.position.y
        let angle = atan2(deltaY, deltaX) - CGFloat(M_PI_2)
        
        let spinAction = SKAction.rotateToAngle(angle, duration:0.3)
        let moveAction = SKAction.moveTo(location, duration: 0.5)
        let actions = SKAction.sequence([spinAction, moveAction])
        
        sprite.runAction(actions)
    }
   
    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>