How To Make a Game Like Space Invaders with Sprite Kit Tutorial: Part 1

Learn how to make a game like Space Invaders in this 2-part Sprite Kit tutorial! By .

Leave a rating/review
Save for later

Update Note 10/29/2014: Check out the updated version of this tutorial using iOS 8 and Swift!

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.

Space Invaders used to be played in big game cabinets in video arcades, chewing up our allowances one quarter at a time. When the Atari 2600 home video game console went to market, Space Invaders was the “killer app” that drove sales of Atari hardware.

In this tutorial, you’ll build an iOS version of Space Invaders using Sprite Kit, the 2D game framework newly introduced in iOS 7.

This tutorial assumes you are familiar with the basics of 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 an iPhone or iPod Touch running iOS 7 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 7 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

Getting Started

Apple provides an XCode 5 template named Sprite Kit 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 template and already has some of the more tedious work done for you.

Once you’ve downloaded and unzipped the project, navigate to the SKInvaders directory and double-click SKInvaders.xcodeproj to open the project in Xcode.

Build and run the project by selecting the Run button from the Xcode toolbar (the first button on the top left) or by using the keyboard shortcut Command + R. You should see the following screen appear on your device or your simulator:

First run - invaders are already watching you

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

The Role of GameScene

To complete your Space Invaders game, you’ll have to code several independent bits of game logic; this tutorial will serve as a great exercise in constructing and refining game logic. It will also reinforce your understanding of how Sprite Kit elements fit together to produce the action in a game.

Most of the action in your game takes place in the stubbed-out GameScene class. You’ll spend most of this tutorial filling out GameScene with your game code.

Let’s take a look at how the game is set up. Open GameViewController.m and scroll down to viewDidLoad. This method is key to all UIKit apps and runs after GameViewController loads its view into memory. It’s intended as a spot for you to further customize your app’s UI at runtime.

Take a look at the interesting parts of viewDidLoad below:

  - (void)viewDidLoad
    // ... omitted ...

    SKView * skView = (SKView *)self.view;

    // ... omitted ...

    //1 Create and configure the scene.
    SKScene * scene = [GameScene sceneWithSize:skView.bounds.size];
    scene.scaleMode = SKSceneScaleModeAspectFill;
    //2 Present the scene.
    [skView presentScene:scene];

The section of code shown above creates and displays the scene as follows:

  1. First, create the scene with the same dimensions as its containing view. scaleMode ensures that the scene is large enough to fill the entire view.
  2. Next, present the scene to draw it on-screen.

Once GameScene is on-screen, it takes over from GameViewController and drives the rest of your game.

Open GameScene.m and take a look at how it’s organized:

  #import "GameScene.h"
  #import "GameOverScene.h"
  #import <CoreMotion/CoreMotion.h>

  #pragma mark - Custom Type Definitions

  #pragma mark - Private GameScene Properties

  @interface GameScene ()
  @property BOOL contentCreated;

  @implementation GameScene

  #pragma mark Object Lifecycle Management

  #pragma mark - Scene Setup and Content Creation

  - (void)didMoveToView:(SKView *)view
      if (!self.contentCreated) {
          [self createContent];
          self.contentCreated = YES;

  - (void)createContent
    // ... omitted ...

  #pragma mark - Scene Update

  - (void)update:(NSTimeInterval)currentTime

  #pragma mark - Scene Update Helpers

  #pragma mark - Invader Movement Helpers

  #pragma mark - Bullet Helpers

  #pragma mark - User Tap Helpers

  #pragma mark - HUD Helpers

  #pragma mark - Physics Contact Helpers

  #pragma mark - Game End Helpers


You’ll notice that there are a lot of #pragma mark - Something or Other type lines in the file. These are called compiler directives since they control the compiler. These particular pragmas are used solely to make the source file easier to navigate.

How do pragmas make source navigation easier, you ask? Notice the area in the bar next to GameScene.m that says “No Selection”, as below:

Where to click for the pragma menu in Xcode

If you click on “No Selection”, a little menu pops up, as so:

The Xcode pragma menu

Hey — that’s a list of all of your pragmas! Click on any pragma to jump to that section of the file. This feature doesn’t look like it has much value at present, but once you’ve added a bunch of invader-killing code, these pragmas will be a really…er… pragmatic way of navigating through your file! :]

Creating the Evil Invaders from Space

Before you start coding, take a moment to consider the GameScene class. When is it initialized and presented on screen? When is the best time to set up your scene with its content?

You might think the scene’s initializer, initWithSize: fits the bill, but the scene may not be fully configured or scaled at the time its initializer runs. It’s better to create a scene’s content once the scene has been presented by a view, since at that point the environment in which the scene operates is “ready to go.”

A view invokes the scene’s didMoveToView: method to present it to the world. Navigate to didMoveToView: and you’ll see the following:

  - (void)didMoveToView:(SKView *)view
      if (!self.contentCreated) {
          [self createContent];
          self.contentCreated = YES;

This method simply invokes createContent using the BOOL property contentCreated to make sure you don’t create your scene’s content more than once. This property is defined in an Objective-C Class Extension found near the top of the file, as below:

    #pragma mark - Private GameScene Properties

    @interface GameScene ()
    @property BOOL contentCreated;

As the pragma points out, this class extension allows you to add “private” properties to your GameScene class, without revealing them to other external classes or code. You still get the benefit of using Objective-C properties, but your GameScene state is stored internally and can’t be modified by other external classes. As well, it doesn’t clutter the namespace of datatypes that your other classes see.

Just as you did in your private scene properties, you can define important constants as private definitions within your file. Navigate to #pragma mark - Custom Type Definitions and add the following code:

typedef enum InvaderType {
} InvaderType;

#define kInvaderSize CGSizeMake(24, 16)
#define kInvaderGridSpacing CGSizeMake(12, 12)
#define kInvaderRowCount 6
#define kInvaderColCount 6
#define kInvaderName @"invader"

The above type definition and constant definitions take care of the following tasks:

  1. Define the possible types of invader enemies. You can use this in switch statements later when you need to do things such as displaying different sprite images for each enemy type. The typedef also makes InvaderType a formal Objective-C type that is type-checked for method arguments and variables. This ensures that you don’t pass the wrong method argument or assign it to the wrong variable.
  2. Define the size of the invaders and that they’ll be laid out in a grid of rows and columns on the screen.
  3. Define a name you’ll use to identify invaders when searching for them in the scene.

It’s good practice to define constants like this rather than using raw numbers like 6 (also known as “magic numbers”) or raw strings like @"invader" (“magic strings”) that are prone to typos. Imagine mistyping @"Invader" where you meant @"invader" and spending hours debugging to find that a simple typo messed everything up. Using constants like kInvaderRowCount and kInvaderName prevents frustrating bugs — and makes it clear to other programmers what these constant values mean.

All right, time to make some invaders! Add the following method to GameScene.m directly after createContent:

-(SKNode*)makeInvaderOfType:(InvaderType)invaderType {
    SKColor* invaderColor;
    switch (invaderType) {
        case InvaderTypeA:
            invaderColor = [SKColor redColor];
        case InvaderTypeB:
            invaderColor = [SKColor greenColor];
        case InvaderTypeC:
            invaderColor = [SKColor blueColor];

    SKSpriteNode* invader = [SKSpriteNode spriteNodeWithColor:invaderColor size:kInvaderSize]; = kInvaderName;

    return invader;

makeInvaderOfType:, as the name implies, creates an invader sprite of a given type. 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 method spriteNodeWithColor:size: of SKSpriteNode to allocate and initialize a sprite that renders as a rectangle of the given color invaderColor with size kInvaderSize.

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 makeInvaderOfType: and place the newly created sprites in the scene.

Still in GameScene.m add the following method directly after makeInvaderOfType::

-(void)setupInvaders {
    CGPoint baseOrigin = CGPointMake(kInvaderSize.width / 2, 180);
    for (NSUInteger row = 0; row < kInvaderRowCount; ++row) {
        InvaderType invaderType;
        if (row % 3 == 0)      invaderType = InvaderTypeA;
        else if (row % 3 == 1) invaderType = InvaderTypeB;
        else                   invaderType = InvaderTypeC;

        CGPoint invaderPosition = CGPointMake(baseOrigin.x, row * (kInvaderGridSpacing.height + kInvaderSize.height) + baseOrigin.y);

        for (NSUInteger col = 0; col < kInvaderColCount; ++col) {
            SKNode* invader = [self makeInvaderOfType:invaderType];
            invader.position = invaderPosition;
            [self addChild:invader];
            invaderPosition.x += kInvaderSize.width + kInvaderGridSpacing.width;

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. 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 current code in createContent with the following:

[self setupInvaders];

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

You've added invaders

The rectangular alien overlords are here! :]