How to Save your Game’s Data: Part 1/2

This tutorial will walk you through how to save your game data – locally on the device and also up in the cloud. By Marin Todorov.

Leave a rating/review
Save for later
Share

Learn how to save your game’s data!

Space game

Unbelievably, in the “old days” many games lacked the ability to save data. You would literally have to beat the game in one sitting! This often caused players to experience emotions ranging from passing frustration to explosive rage, as Angry Video Game Nerd can attest.

In the great days of modern iOS, your game has no excuse because there are plenty of ways to save data, from NSUserDefaults to Core Data to iCloud and more!

In this two-part tutorial, you’re going to learn the best ways to save data in your games. Specifically, you’ll learn how to:

  • Separate your data: You’ll build a class to hold your game data and update it properly during gameplay.
  • Save your data: You’ll use NSCoding to persist your game data to the device.
  • Use iCloud: You’ll also learn how to use iCloud to allow the user to continue their game on a different device if they so desire.

By the end of both parts, you’ll have an awesome iCloud connected Space Shooter made with Sprite Kit:

sgd_01_completed

And yes, that’s a cat in the cockpit. Surely you know cats are avid pilots?

Get ready to start saving!

Getting Started

For this tutorial, you’ll build upon the game created in Tony Dahbura‘s Sprite Kit Space Shooter Tutorial. These exercises will be most useful if you go through that tutorial first, but if you’d rather not, at least scan the code so you’re familiar with how it works.

If you decide to skip the Space Shooter tutorial, you can just download the completed project.

Open up Space Shooter in Xcode, then build and run on an iOS device. You’ll need to run the game on an actual device since the accelerometer is required to steer the spaceship around all those asteroids.
sgd_02_start

To win the game just stay alive for 30 seconds. All you have to do is blast the asteroids out of your way to avoid deadly collisions. It’s not as easy as it sounds!

Game Data Class

Did you notice that the game doesn’t track the score, or the distance traveled by the ship? You’ll start by adding tracking for those two metrics so that you have something interesting to save across gameplay sessions.

In this section, you’ll create a new class to hold the game data. It’s a best practice to isolate your game’s data from the rest of the game; this abstraction makes it much easier to save the user’s progress from game to game.

In Xcode, go to File\New\File…, choose the iOS\Cocoa Touch\Objective-C class template and click Next. Name your new class RWGameData, make it a subclass of NSObject, click Next and then Create.

RWGameData will hold your game’s data, specifically, the score and distance traveled for the current game, as well as the player’s highest score and total distance flown. Open RWGameData.h and replace the default contents of this file with the following code:

#import <Foundation/Foundation.h>

@interface RWGameData : NSObject

@property (assign, nonatomic) long score;
@property (assign, nonatomic) int distance;

@property (assign, nonatomic) long highScore;
@property (assign, nonatomic) long totalDistance;

+(instancetype)sharedGameData;
-(void)reset;

@end

Here’s what each of those class properties will do:

  • score: The current game’s score
  • distance: The current game’s distance traveled (in space miles)
  • highScore: The player’s high score across all games (later on in this tutorial you’ll persist this high score)
  • totalDistance: The player’s total distance travelled across all games

Keep in mind that the logic necessary to keep this data separate by player and device will be more complex than just keeping everything in memory as you’re doing for now.

There are also two method definitions in your class interface. sharedGameData will give you access to this class’ singleton instance. As you may have guessed, reset will reset the current game data.

Open RWGameData.m and add the following method:

+ (instancetype)sharedGameData {
    static id sharedInstance = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    
    return sharedInstance;
}

This is a boilerplate Objective-C implementation of the singleton pattern. Using sharedGameData throughout your app will ensure that you’re always accessing the same RWGameData instance. You can use this same code any time you want to provide singleton access to one of your own classes.

Next, take care of the reset method, which will reset the game’s score and distance whenever the user starts a new game. Add this method inside RWGameData.m:

-(void)reset
{
    self.score = 0;
    self.distance = 0;
}

In this method you just set the score and distance properties to 0, so the user can start fresh.

Pimping up the HUD

Now that you have a class to store the game’s data, now you’ll modify the game to keep score with the new class.

Open MyScene.m, where the game logic resides, and add the following line at the top of the file to import your new class:

#import "RWGameData.h"

You’ll need a few labels to display progress to the user. Add the following instance variables to the @implementation section in MyScene.m:

SKLabelNode* _score;
SKLabelNode* _highScore;
SKLabelNode* _distance;

Still in MyScene.m, add the following method to initialize those labels and add them to the scene:

-(void)setupHUD
{
    _score = [[SKLabelNode alloc] initWithFontNamed:@"Futura-CondensedMedium"];
    _score.fontSize = 12.0;
    _score.position = CGPointMake(50, 7);
    _score.fontColor = [SKColor greenColor];
    [self addChild:_score];

    _distance = [[SKLabelNode alloc] initWithFontNamed:@"Futura-CondensedMedium"];
    _distance.fontSize = 12.0;
    _distance.position = CGPointMake(115, 7);
    _distance.fontColor = [SKColor orangeColor];
    [self addChild:_distance];

    _highScore = [[SKLabelNode alloc] initWithFontNamed:@"Futura-CondensedMedium"];
    _highScore.fontSize = 12.0;
    _highScore.position = CGPointMake(200, 7);
    _highScore.fontColor = [SKColor redColor];
    [self addChild:_highScore];
}

This code sets up _score, _distance, and _highScore by setting font face, size and color, and finally positioning all three labels at the bottom of the scene, out of the way of the space ship.

Next, you’ll need to call setupHUD: somewhere in your code. Add the following line to initWithSize:, just before the line that calls startBackgroundMusic:

[self setupHUD];

This will add the HUD labels to the scene just before starting the gameplay.

One final touch — at the end of startTheGame:, add the code to initialize the label’s values:

_highScore.text = [NSString stringWithFormat:@"High: %li pt", [RWGameData sharedGameData].highScore];
_score.text = @"0 pt";
_distance.text = @"";

This will set the high score label to whatever you stored last in RWGameData‘s highScore property and will reset the values of _score and _distance. You’re all ready to go! Build and run the project right now.

sgd_03_initialHUD

The HUD is now visible, but the game doesn’t keep score yet – that’s the next thing to add to the app.