How To Make a Game Like Space Invaders with SpriteKit and Swift: Part 1

Learn how to make a game like Space Invaders using Apple’s built-in 2D game framework: Sprite Kit! By Ryan Ackermann.

Leave a rating/review
Save for later
Share

Update 10/11/16: This tutorial has been updated for Xcode 8 and Swift 3.

Update 10/11/16: This tutorial has been updated for Xcode 8 and Swift 3.

Space Invaders is one of the most important video games ever developed. Created by Tomohiro Nishikado and released in 1978 by Taito Corporation, it earned billions of dollars in revenue. It became a cultural icon, inspiring legions of non-geeks to take up video games as a hobby.

In this tutorial, you’ll build an iOS version of Space Invaders, using Swift and Sprite Kit, Apple’s 2D game framework.

This tutorial assumes you are familiar with the basics of Swift and Sprite Kit. If you are completely new to Sprite Kit, you should go through our Sprite Kit tutorial for beginners first.

Also, you will need Xcode 8 or later, an iPhone or iPod Touch running iOS 10 and an Apple developer account in order to get the most out of this tutorial. That is because you will be moving the ship in this game using the accelerometer, which is not present on the iOS simulator. If you don’t have an iOS 10 device or developer account, you can still complete the tutorial — you just won’t be able to move your ship.

Without further ado, let’s get ready to blast some aliens!

An original Space Invaders arcade cabinet

An original Space Invaders arcade cabinet

An original Space Invaders arcade cabinet

Getting Started

Apple provides an template named Game which is pretty useful if you want to create your next smash hit from scratch. However, in order to get you started quickly, download the starter project for this tutorial. It’s based on the Sprite Kit Game template and already has some of the more tedious work done for you.

Once you’ve downloaded and unzipped the project, open the project in Xcode and build and run. After the splash screen intro, you should see the following screen appear on your device or your simulator:

space_invaders_first_run

Creepy – the invaders are watching you! However, if you see the screen above, this means you’re ready to get started.

Adding Colorful Invaders

All right, time to make some invaders! In this game, you’re going to create three types of invaders, so open GameScene.swift and add this enumeration inside the GameScene class, right after the line var contentCreated = false:

enum InvaderType {
  case a
  case b
  case c
  
  static var size: CGSize {
    return CGSize(width: 24, height: 16)
  }
  
  static var name: String {
    return "invader"
  }
}

Here you create an enumeration with three different cases, and a few helper properties to return the size of the invader sprite, and a description.

Next, add this new method directly after createContent():

func makeInvader(ofType invaderType: InvaderType) -> SKNode {
  // 1
  var invaderColor: SKColor
  
  switch(invaderType) {
  case .a:
    invaderColor = SKColor.red
  case .b:
    invaderColor = SKColor.green
  case .c:
    invaderColor = SKColor.blue
  }
  
  // 2
  let invader = SKSpriteNode(color: invaderColor, size: InvaderType.size)
  invader.name = InvaderType.name
  
  return invader
}

You take the following actions in the above code:

  1. Use the invaderType parameter to determine the color of the invader.
  2. Call the handy convenience initializer SKSpriteNode(color:size:) to initialize a sprite that renders as a rectangle of the given color invaderColor with the enumeration’s size InvaderType.size.

Okay, so a colored block is not the most menacing enemy imaginable. It may be tempting to design invader sprite images and dream about all the cool ways you can animate them, but the best approach is to focus on the game logic first, and worry about aesthetics later.

Adding makeInvaderOfType() isn’t quite enough to display the invaders on the screen. You’ll need something to invoke this method and place the newly created sprites in the scene.

First, you’ll need a few constants. Add the following constants inside GameScene, right after the declaration of InvaderType:

let kInvaderGridSpacing = CGSize(width: 12, height: 12)
let kInvaderRowCount = 6
let kInvaderColCount = 6

These are some constants to represent how you’ll be laying out the invaders in this game – basically a 6×6 grid, with 12×12 point spacing in-between each invader.

Next add the following method directly after makeInvader(ofType:):

func setupInvaders() {
  // 1
  let baseOrigin = CGPoint(x: size.width / 3, y: size.height / 2)
  
  for row in 0..<kInvaderRowCount {
    // 2
    var invaderType: InvaderType
    
    if row % 3 == 0 {
      invaderType = .a
    } else if row % 3 == 1 {
      invaderType = .b
    } else {
      invaderType = .c
    }
    
    // 3
    let invaderPositionY = CGFloat(row) * (InvaderType.size.height * 2) + baseOrigin.y
    
    var invaderPosition = CGPoint(x: baseOrigin.x, y: invaderPositionY)
    
    // 4
    for _ in 1..<kInvaderColCount {
      // 5
      let invader = makeInvader(ofType: invaderType)
      invader.position = invaderPosition
      
      addChild(invader)
      
      invaderPosition = CGPoint(
        x: invaderPosition.x + InvaderType.size.width + kInvaderGridSpacing.width,
        y: invaderPositionY
      )
    }
  }
}

The above method lays out invaders in a grid of rows and columns. Each row contains only a single type of invader. The logic looks complicated, but if you break it down, it makes perfect sense:

  1. Declare and set the baseOrigin constant (representing the origin of where to start spawning areas - 1/3 from the right of the screen, and 1/2 from the bottom of the screen) and loop over the rows.
  2. Choose a single InvaderType for all invaders in this row based on the row number.
  3. Do some math to figure out where the first invader in this row should be positioned.
  4. Loop over the columns.
  5. Create an invader for the current row and column and add it to the scene.
  6. Update the invaderPosition so that it's correct for the next invader.

Now, you just need to display the invaders on the screen. Replace the temporary invader setup code in createContent() with setupInvaders() just above the background color:

setupInvaders()

Build and run your app; you should see a bunch of invaders on the screen, as shown below:

space_invaders_added_color

The rectangular alien overlords are here! :]

Create Your Valiant Ship

With those evil invaders on screen, your mighty ship can't be far behind. Just as you did for the invaders, you first need to define a few constants.

Add the following code immediately below the kInvaderColCount line:

let kShipSize = CGSize(width: 30, height: 16)
let kShipName = "ship"

kShipSize stores the size of the ship, and kShipName stores the name you will set on the sprite node, so you can easily look it up later.

Next, add the following two methods just after setupInvaders():

func setupShip() {
  // 1
  let ship = makeShip()
  
  // 2
  ship.position = CGPoint(x: size.width / 2.0, y: kShipSize.height / 2.0)
  addChild(ship)
}

func makeShip() -> SKNode {
  let ship = SKSpriteNode(color: SKColor.green, size: kShipSize)
  ship.name = kShipName
  return ship
}

Here's the interesting bits of logic in the two methods above:

  1. Create a ship using makeShip(). You can easily reuse makeShip() later if you need to create another ship (e.g. if the current ship gets destroyed by an invader and the player has "lives" left).
  2. Place the ship on the screen. In Sprite Kit, the origin is at the lower left corner of the screen. The anchorPoint is based on a unit square with (0, 0) at the lower left of the sprite's area and (1, 1) at its top right. Since SKSpriteNode has a default anchorPoint of (0.5, 0.5), i.e., its center, the ship's position is the position of its center. Positioning the ship at kShipSize.height / 2.0 means that half of the ship's height will protrude below its position and half above. If you check the math, you'll see that the ship's bottom aligns exactly with the bottom of the scene.

To display your ship on the screen, add the following line below setupInvaders() in createContent():

setupShip()

Build and run your app; and you should see your ship arrive on the scene, as below:

space_invaders_added_player

Fear not, citizens of Earth! Your trusty spaceship is here to save the day!