By Kevin PhamDecember 19, 2014

Simple Sprite Kit Tutorial in Swift, Part 2

In the first part of this tutorial, we created the basic scrolling background and flying spaceship. In this part, we are going to add the oncoming missiles that the spaceship can collide with, and the end game screen when the spaceship collides with these missiles.

The source code for part 2 is on GitHub.

Sprite Kit game in Swift

Missiles and The End

Let’s start by adding the method to display missiles on the screen. Add this method at the end in GameScene.swift:

func addMissile() {
    // Initializing spaceship node
    var missile = SKSpriteNode(imageNamed: "red-missile")
    missile.setScale(0.15)

    // Adding SpriteKit physics body for collision detection
    missile.physicsBody = SKPhysicsBody(rectangleOfSize: missile.size)
    missile.physicsBody?.categoryBitMask = UInt32(obstacleCategory)
    missile.physicsBody?.dynamic = true
    missile.physicsBody?.contactTestBitMask = UInt32(shipCategory)
    missile.physicsBody?.collisionBitMask = 0
    missile.physicsBody?.usesPreciseCollisionDetection = true
    missile.name = "missile"

    // Selecting random y position for missile
    var random : CGFloat = CGFloat(arc4random_uniform(300))
    missile.position = CGPointMake(self.frame.size.width + 20, random)
    self.addChild(missile)
}

For an explanation of each line, take a look at the addShip method, explained in part 1. Once you have added this method, instantiate it after the addShip method in didMoveToView.

self.addShip()
self.addMissile()

Run the code to see if the missile is appearing. You’ll only see the tip of the missile on the right edge. It’s because the sprite is being spawned off screen. The goal is to move it to the left until it’s off the screen again.

Adding the Missile

Now we want to move the missile on the screen so that our spaceship can dodge it. Add the missile’s velocity after the background speed. We want the missile to move a little bit faster than the background.

let backgroundVelocity : CGFloat = 3.0
let missileVelocity : CGFloat = 5.0

Next we are going to add the method to move the missile at the end of GameScene.swift.

func moveObstacle() {
    self.enumerateChildNodesWithName("missile", usingBlock: { (node, stop) -> Void in
        if let obstacle = node as? SKSpriteNode {
            obstacle.position = CGPoint(x: obstacle.position.x - self.missileVelocity, y: obstacle.position.y)
            if obstacle.position.x < 0 {
                obstacle.removeFromParent()
            }
        }
    })
}

This code is similar to the one that moved the background, but for only the child nodes named “missile.”

We’re going to call this method in the update method like we did for our moveBackground method. Our update method should look like this now:

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */

    self.moveBackground()
    self.moveObstacle()
}

We want to add the missiles randomly at different y-axis positions every second. We are going to use our update method to keep track of the lastMissileAdded time interval. Update the method with this code:

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
    if currentTime - self.lastMissileAdded > 1 {
        self.lastMissileAdded = currentTime + 1
        self.addMissile()
    }

    self.moveBackground()
    self.moveObstacle()
}

Run the project. There should be multiple missiles on the screen.

Missiles and Hero

Now we need to end the game if the spaceship collides with the missiles. To do that, we need to add this method at the end of GameScene.swift:

func didBeginContact(contact: SKPhysicsContact) {
    var firstBody = SKPhysicsBody()
    var secondBody = SKPhysicsBody()

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }

    if (firstBody.categoryBitMask & UInt32(shipCategory)) != 0 && (secondBody.categoryBitMask & UInt32(obstacleCategory)) != 0 {
        ship.removeFromParent()
    }
}

Since we set the scene as the contact delegate of the physics world earlier, this method will be called whenever two physics bodies collide.

There are two parts to this method:

  1. This method passes the two bodies that collide, but does not guarantee that they are passed in any particular order. This bit of code just arranges them. They are sorted by their category bit masks, so you can make some assumptions later.
  2. Finally, it checks to see if the two bodies that collide are the spaceship and missile, and if so, calls the method you wrote earlier.

If you run the current code, you will see the spaceship disappear if it collides with the missile.

Now let’s create a new scene and layer that will serve as your “End Game” indicator. Create a new file with the iOS\Source\Cocoa Touch Class template. Name the class GameOverScene, make it a subclass of SKScene, and set the language to Swift. Click Next and then Create.

Then replace the code in GameOverScene.swift with the following:

import UIKit
import SpriteKit

class GameOverScene: SKScene {

    override init(size: CGSize) {
        super.init(size: size)

        //1
        self.backgroundColor = SKColor.whiteColor()

        //2
        let message = "Game over"

        //3
        var label = SKLabelNode(fontNamed: "Chalkduster")
        label.text = message
        label.fontSize = 40
        label.fontColor = SKColor.blackColor()
        label.position = CGPointMake(self.size.width/2, self.size.height/2)
        self.addChild(label)

        //4
        let replayMessage = "Replay Game"
        var replayButton = SKLabelNode(fontNamed: "Chalkduster")
        replayButton.text = replayMessage
        replayButton.fontColor = SKColor.blackColor()
        replayButton.position = CGPointMake(self.size.width/2, 50)
        replayButton.name = "replay"
        self.addChild(replayButton)
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)
            let node = self.nodeAtPoint(location) //1
            if node.name == "replay" { //2
                let reveal : SKTransition = SKTransition.flipHorizontalWithDuration(0.5)
                let scene = GameScene(size: self.view!.bounds.size)
                scene.scaleMode = .AspectFill
                self.view?.presentScene(scene, transition: reveal)
            }
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

There are four parts to the scene initializer:

  1. Sets the background color to white, same as you did for the main scene.
  2. Sets the message to “Game Over.”
  3. Displays the Game Over label node, choosing font and size.
  4. Creates another label node to represent our “Replay” button.

To track when the user hits the replay button, we need to utilize the touchesBegan method. This method is called when the user touches anywhere on the screen. Let’s break down this method:

  1. We track the node present at the location where the user touched the screen.
  2. If the name of the node that is touched is equal to “replay,” then it means the user has hit the replay button.
  3. We transition back to GameScene to restart the game. You can pick from a variety of different animated transitions for how we want the scenes to display—we choose a flip transition here that takes 0.5 seconds. Then you will create the scene you want to display, and use the presentScene:transition: method on the self.view property.

Just one more thing to do before our game is ready to play. In GameScene.swift, after removing the spaceship when a collision occurs, we need to transition to the GameOverScene. So replace didBeginContact with the following code:

func didBeginContact(contact: SKPhysicsContact) {
    var firstBody = SKPhysicsBody()
    var secondBody = SKPhysicsBody()

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }

    if (firstBody.categoryBitMask & UInt32(shipCategory)) != 0 && (secondBody.categoryBitMask & UInt32(obstacleCategory)) != 0 {
        ship.removeFromParent()
        let reveal = SKTransition.flipHorizontalWithDuration(0.5)
        let scene = GameOverScene(size: self.size)
        self.view?.presentScene(scene, transition: reveal)
    }
}

Run the game. You should see the Game Over screen if our hero collides with a missile.

Game Over scene

El fin! Here’s the full source code for this SpriteKit tutorial. I hope you enjoyed it!


Get job-ready in Swift »


This tutorial originally appeared on Kevin’s blog and is shared with permission. It was written in Swift and checked against Xcode 6.1.1 and iOS 8.1. Thanks to Megha Gulati for posting the original tutorial in Objective-C.