Achievements

Achievements are a great way to track what a player has done in your game and to give the player more incentive to keep playing your game. An achievement represents a quantitative goal that the player can accomplish in your game. As the local player plays your game, they make progress towards completing the achievement. When the player meets or exceeds the goal, the achievement is considered earned, and the player is rewarded. Your game defines the goal and the game mechanics that describe how a player earns the achievement. In practice, Game Center does not need to know anything about your game design; it only knows what progress the player has made towards an achievement.

In Game Center, an achievement earns a player achievement points. When you define an achievement, you decide how many points it is worth. A player can see the total number of points that can potentially be earned in your game as well as how many points he or she has currently earned. The Game Center app allows players to compare their achievements with those of friends; this comparison screen includes the total points earned.

When you add an achievement to your game, you configure how the achievement is granted and how it is described to the player. You also design the game mechanics that allow the player to make progress towards completing the achievement.

Checklist for Supporting Achievements

To add achievements to your game, you need to take the following steps:

  1. Before you add achievements, add code to authenticate the local player. See “Working with Players in Game Center.”

  2. Design your game’s achievements. See “Designing an Achievement.”

  3. Go to iTunes Connect and configure the achievements for your game. You provide all of the data needed to display achievements to the player. See “Configuring Achievements in iTunes Connect.”

  4. Add code to report the local player’s progress to Game Center. By storing the progress on Game Center, you also make the player’s progress visible in the Game Center app. See “Reporting Achievement Progress to Game Center.”

  5. Add code to load the local player’s previous progress on achievements from Game Center. The local player’s progress should be loaded shortly after the player is successfully authenticated. See “Loading Achievement Progress.”

  6. Add code to display the player’s progress towards achievements. See “Displaying the Standard Achievement User Interface” to see how your game can display the standard achievements screen.

    Optionally, you can retrieve achievement data from Game Center and use it to create your own custom achievement user interface. See “Creating a Custom Achievement User Interface.”

Designing an Achievement

The design of an achievement starts with a simple statement of the achievement’s goal. Here are some example goals that can define a game:

A goal statement describes a triggering condition (“Earn first place on a different race track”) and a quantity (5). The triggering condition is essential as it defines something your game logic needs to track. In the racing example, your game must check the victory conditions at the end of each race, but it also needs to record which tracks a player has defeated.

The quantity usually defines how granular the task is for the player. With some achievements, quantity is irrelevant; the achievement is either earned or not. For example, “Defeat the end-game boss” is not a goal that the player generally earns partial credit towards completing. In contrast, a goal of “Capture 10 pirates” is a more granular task. Your game needs to track the number of pirates captured and use this count to report progress to Game Center.

When you design an achievement, consider a few other characteristics: value, visibility, and repeatability. These characteristics affect more than the design of your game; later, when you define your achievement in iTunes Connect, you’ll set attributes based on these characteristics:

You have a lot of room to be creative with the kinds of achievements you create. Here are some common strategies for designing achievements that can guide you through the process.

Create Achievements That Show the Different Things Players Can Do in Your Game

When experienced players purchase new games, they examine the list of achievements to see what’s possible in those games. Your list of achievements should strongly communicate what you want players to think about or do while playing your game. You want achievements to provide a variety of different goals a player can strive towards. When you provide a variety of goals and achievements, players continue to play your game.

Create Achievements That Require Different Levels of Skill or Dedication

Provide achievements for both new players and experienced players—and the players in between. Achievements are a reward to the player for playing your game. If all of your game’s achievements are too easy to beat, the rewards will seem cheap and the player won’t feel challenged. On the other hand, if all the achievements are too difficult to earn, then players may not be rewarded often enough to continue playing your game. Strive to include achievements that can be earned when learning how to play the game and also achievements that reward players for learning how to play your game well.

Another reason to include more difficult challenges is that they encourage players to use them when challenging other players. See “Challenges.”

Create Achievements for Different Sections Or Modes Present in Your Game

For various reasons, you might choose to partition the gameplay into smaller games or game modes. Some reasons include:

  • Distinct sets of game rules or rule variants (capture the flag, survival)

  • Distinct game levels or maps

  • A game with an underlying story separated into distinct acts or story arcs

If your game does partition its gameplay into smaller mini-games, define achievements for each, because it encourages players to try each of the game modes or to complete your game.

Use Hidden Achievements to Delight and Reward the Player

Hidden achievements should be used sparingly. Usually, you want players to know what’s expected of them in your game, so that they can actively set goals for themselves. And hidden achievements cannot be used to create new challenges. However, a hidden achievement can be a great opportunity to surprise a player with something unexpected. For example, any of the following might be places where a hidden achievement could be helpful:

  • An unexpected element in the plot or a story.

  • The player succeeds at a crazy stunt.

  • You can flip the convention from an achievement being something the player tries hard to earn into something more ignoble, earned when they fail spectacularly. The achievement then represents a way to inject humor into the situation. For example, an achievement called Hot Foot that might appear after the player has fallen into lava 20 times.

Save Some of Your Budget for Later Versions of Your Game

There are limits to how many achievements you can create in your game and how many points you can reward to the player:

  • Each game can have up to 100 achievements.

  • Each game can award up to 1000 points.

  • No individual achievement can award more than 100 points.

Avoid spending all of your budget on the initial version of your game. Save some of your budget to support updates and new content.

Configuring Achievements in iTunes Connect

When you are ready to add achievements, you define them first in iTunes Connect. For each achievement, you provide values for all of the properties listed in Table 5-1. Further, for each language you plan to localize the achievement into, you provide data for all of the properties listed in Table 5-2.

Table 5-1  Achievement properties

Property

Description

Achievement Reference Name

An internal name that you must provide for each achievement, used only in iTunes Connect. This is the name you will use if you search for the achievement in iTunes Connect.

Achievement ID

A chosen alphanumeric identifier for your achievement. This ID is limited to 100 characters. Your achievement ID is a permanent setting and therefore cannot be edited at a later date. The achievement ID is used inside your game to refer to this achievement.

Point Value

The points that your achievement is worth.

Hidden

Achievements marked as Hidden remain hidden to a player on Game Center until after the first time you report progress for that achievement.

Achievable More Than Once

Indicates whether the user can earn the achievement multiple times.

Table 5-2  Achievement language properties

Property

Description

Language

The language in which you would like this achievement to appear.

Title

The localized title of this achievement as you would like it to appear in Game Center.

Pre-earned Description

The description of your achievement as it appears to a Game Center player before he or she earns it.

Earned Description

The description of your achievement as it appears to a Game Center player after he or she earns it.

Image

A localized image that represents the achievement. The image must be a .jpeg, .jpg, .tif, .tiff, or .png file that is 512 x 512 or 1024 x 1024 pixels, at least 72 dpi, and in the RGB color space. This property is required.

For more information on setting up your leaderboards in iTunes Connect, see “Achievements” in Game Center Configuration Guide for iTunes Connect.

Adding Achievement Support to Your Game

When you are ready to implement achievements in your game, you do so using the classes listed in Table 5-3.

Table 5-3  Classes in Game Kit used to implement achievement support

Class Name

Description

GKAchievement

Holds information about the player’s progress towards completing an achievement. Your game creates GKAchievement objects to report progress to Game Center. Game Kit can also load the current progress stored on Game Center and return it to your game using GKAchievement objects.

GKAchievementDescription

Holds the localized text and images for an achievement. The data for achievement descriptions is retrieved from Game Center at runtime and is based on the data you provided to iTunes Connect when you created your achievement there.

GKGameCenterViewController

Provides a standard user interface to display Game Center content to the player. This content includes an achievements page.

GKAchievementViewController

Provides a standard user interface to display an achievements page. If the GKGameCenterViewController class is available, you should use that class instead.

Reporting Achievement Progress to Game Center

Your game should report progress to Game Center whenever the player makes progress towards completing an achievement. By storing progress information in Game Center, you gain a few benefits:

  • The Game Center app can display the player’s progress towards the achievement.

  • If a player has multiple devices, your game on another device can retrieve the player’s progress towards achievements.

Your game reports the player’s progress by using a floating-point percentage, from 0.0 to 100.0, that represents how much of the achievement the player has completed. You decide how that percentage is calculated and when it changes. For example, if the player earns an achievement simply for discovering a location in your game, then you would simply report the achievement as 100 percent complete the first time you report progress to Game Center. On the other hand, for an achievement like “Capture 10 pirates”, your reporting mechanism increments by 10 percent each time the player captures a pirate.

When you report progress to Game Center, two things happen:

  • If the achievement was previously hidden, it is revealed to the player. The achievement is revealed even if your player has made no actual progress on the achievement (a percentage of 0.0).

  • If the reported value is higher than the previous value reported for the achievement, the value on Game Center is updated to the new value. Players never lose progress on achievements.

When the progress reaches 100 percent, the achievement is marked as completed, and both the image and completed description appear when the player views the achievements screen.

Listing 5-1 shows how to report progress to Game Center. First, a new achievement object is initialized using an identifier string for an achievement. Next, the object’s percentComplete property is set to reflect the player’s progress. Finally, the object’s reportAchievementWithCompletionHandler: method is called, passing in a block to be notified when the report is sent.

Listing 5-1  Reporting progress on an achievement

- (void) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent
{
    GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier: identifier];
    if (achievement)
    {
         achievement.percentComplete = percent;
         [achievement reportAchievementWithCompletionHandler:^(NSError *error)
             {
                  if (error != nil)
                  {
                      NSLog(@"Error in reporting achievements: %@", error);
                  }
             }];
    }
}

In iOS 7, you can report achievements for other players when ending a turn-based match. Only the current player is able to report achievements.

Listing 5-2  Reporting progress on an achievement for another player

- (void) reportAchievementIdentifier: (NSString*) identifier forPlayer: (NSString *) playerID percentComplete: (float) percent
{
    GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:identifier forPlayer:playerID];
    if (achievement)
    {
         achievement.percentComplete = percent;
         [achievement reportAchievementWithCompletionHandler:^(NSError *error)
             {
                  if (error != nil)
                  {
                      NSLog(@"Error in reporting achievements: %@", error);
                  }
             }];
    }
}

To report progress on multiple achievements at once, use the reportAchievements:withCompletionHandler: class method instead. Listing 5-3 shows a possible implementation. This example creates three achievement objects representing three achievements the player has just completed. It then bundles the achievement objects into an array and reports them all with a call to the class method.

Listing 5-3  Reporting progress on multiple achievements

- (void) completeMultipleAchievements
{
    GKAchievement *achievement1 = [[GKAchievement alloc] initWithIdentifier: @"DefeatedFinalBoss"];
    GKAchievement *achievement2 = [[GKAchievement alloc] initWithIdentifier: @"FinishedTheGame"];
    GKAchievement *achievement3 = [[GKAchievement alloc] initWithIdentifier: @"PlayerIsAwesome"];
    achievement1.percentComplete = 100.0;
    achievement2.percentComplete = 100.0;
    achievement3.percentComplete = 100.0;
 
    NSArray *achievementsToComplete = [NSArray arrayWithObjects:achievement1,achievement2,achievement3, nil];
    [GKAchievement reportAchievements: achievementsToComplete withCompletionHandler:^(NSError *error)
             {
                  if (error != nil)
                  {
                      NSLog(@"Error in reporting achievements: %@", error);
                  }
             }];
    }
}

Whether reporting progress on a single achievement or on multiple achievements at once, your game rarely needs to do anything specific when an error occurs. If an error occurs, such as when a network is not available, Game Kit automatically resends the data at an appropriate time.

Loading Achievement Progress

You load the local player’s current progress information from Game Center by calling the loadAchievementsWithCompletionHandler: method. If the operation completes successfully, it returns an array of GKAchievement objects, one object for each achievement your game previously reported progress for.

Listing 5-4 shows how your game might implement this.

Listing 5-4  Loading achievement progress

- (void) loadAchievements
{    [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
        if (error != nil)
        {
            // Handle the error.
        }
        if (achievements != nil)
        {
            // Process the array of achievements.
        }
       }];
}

A logical time to load the local player’s progress is immediately after the player is authenticated.

As the player progresses through your game, you want to update their progress on Game Center. If your game has previously reported progress towards this achievement, your game should first load the player’s progress from Game Center so that you know what progress the player has already made. If the player has made progress on an achievement that the player has never made progress on before, your game should create a new achievement object. An easy way to manage these achievement objects in your game is by using a mutable dictionary, using the identifier property as a dictionary key, and the achievement object as the contents for that key. Here’s how to modify Listing 5-1 and Listing 5-4 to use a dictionary:

  1. Add a mutable dictionary property to your class that reports achievements; this dictionary stores the collection of achievement objects.

    @property(nonatomic, retain) NSMutableDictionary *achievementsDictionary;
  2. Initialize the achievements dictionary.

    achievementsDictionary = [[NSMutableDictionary alloc] init];
  3. When your game loads achievement data, add the achievement objects to the dictionary.

    - (void) loadAchievements
    {
        [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error)
            {
                if (error == nil)
                {
                    for (GKAchievement* achievement in achievements)
                        [achievementsDictionary setObject: achievement forKey: achievement.identifier];
                }
            }];
    }
  4. Implement a method that tests the dictionary for a particular achievement identifier. If the identifier does not exist as a key in the dictionary, create a new achievement object and add it to the dictionary.

    - (GKAchievement*) getAchievementForIdentifier: (NSString*) identifier
    {
        GKAchievement *achievement = [achievementsDictionary objectForKey:identifier];
        if (achievement == nil)
        {
            achievement = [[GKAchievement alloc] initWithIdentifier:identifier];
            [achievementsDictionary setObject:achievement forKey:achievement.identifier];
        }
        return achievement;
    }
  5. Modify the code in Listing 5-1 to call getAchievementForIdentifier: to retrieve the achievement object.

    - (void) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent
    {
        GKAchievement *achievement = [self getAchievementForIdentifier:identifier];
        if (achievement)
        {
             achievement.percentComplete = percent;
             [achievement reportAchievementWithCompletionHandler:^(NSError *error)
                 {
                      if (error != nil)
                      {
                          // Log the error.
                      }
                 }];
        }
    }

Best Practices for Reporting Achievement Progress

When designing a game that uses Game Center achievements, you need to balance the need to be responsive to the player against your goal to use as few device resources as possible. With those two concepts in mind, consider the following practices:

  • Report progress on an achievement as soon as the player makes progress. Don’t delay reporting until a later time; if a player earns an achievement, the banner should be displayed immediately.

  • Report progress only when the player has actually made further progress. Do not report changes to Game Center if the player has not made progress, because it consumes network resources without actually changing the state of the data on Game Center.

  • If the player makes progress on multiple achievements simultaneously, use the technique shown in Listing 5-3 to report the progress. Using this class method is more efficient.

  • If your game displays a custom banner or indicator when a player earns an achievement, set the showsCompletionBanner property to NO before reporting the achievement to Game Center.

Resetting Achievement Progress

You may want to allow the player to reset their progress on achievements in your game. Listing 5-5 demonstrates how to do this. First, this method clears any locally cached achievement objects created by the previous example. Then, it calls Game Kit to reset the player’s progress stored on Game Center.

Listing 5-5  Resetting achievement progress

- (void) resetAchievements
{
// Clear all locally saved achievement objects.
    achievementsDictionary = [[NSMutableDictionary alloc] init];
// Clear all progress saved on Game Center.
[GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error)
    {
        if (error != nil)
            // handle the error.
    }];
}

When your game resets a player’s progress on achievements, all progress information is lost. Hidden achievements, if previously shown, are hidden again until your game reports progress on them. For example, if the only reason those achievements were originally hidden was that they were associated with an In-App Purchase, then you would reveal those achievements again.

Displaying the Standard Achievement User Interface

In addition to sending achievement progress to Game Center, you should also allow players to view their progress from within your game. The simplest way to do this is with a GKGameCenterViewController object. You can adjust the behavior of a Game Center view controller so that it shows the achievements page when presented. Listing 5-6 shows how to accomplish this. It instantiates a new view controller and sets its gameCenterDelegate property to point to the presenting view controller. It then configures the controller’s view state to show the achievements page. Finally, it presents the leaderboard and waits for the delegate to be called. On OS X, you use the GKDialogController to display the view controller, as described in “Displaying Game Center User Interface Elements.”

Listing 5-6  Displaying the achievement’s page of the Game Center user interface.

- (void) showLeaderboard: (NSString*) leaderboardID
{
    GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
    if (gameCenterController != nil)
    {
        gameCenterController.gameCenterDelegate = self;
        gameCenterController.viewState = GKGameCenterViewControllerStateAchievements;
        [self presentViewController: gameCenterController animated: YES completion:nil];
    }
}

When the player finishes looking at the leaderboard, the delegate is called. Listing 5-7 shows a typical implementation, which simply dismisses the presented view controller.

Listing 5-7  Responding when the player dismisses the Game Center content

- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

Creating a Custom Achievement User Interface

When creating your own custom achievement user interface, load the data initially provided to iTunes Connect into your game. The data is returned to your game using GKAchievementDescription objects.

Loading achievement descriptions is a two-step process. First, your game loads the text descriptions for all achievements in your game. Then, when an achievement is completed and you want to display the completed image, you explicitly load that image. This allows your game to load only images you need, which reduces its memory footprint.

Listing 5-8 shows how to load the achievement descriptions using the loadAchievementDescriptionsWithCompletionHandler: method.

Listing 5-8  Retrieving achievement metadata from Game Center

- (void) retrieveAchievmentMetadata
{
    [GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:
        ^(NSArray *descriptions, NSError *error) {
            if (error != nil)
            {
                // Process the error.
            }
            if (descriptions != nil)
            {
                // use the achievement descriptions.
            }
        }];
}

Although the properties are self-explanatory, one critical property worth noting is the identifier property. This corresponds to the achievement identifier used in iTunes Connect and on a GKAchievement object. When you design your custom user interface, you use the identifier property to match each GKAchievementDescription object to the corresponding GKAchievement object that records the player’s progress on that achievement.

The value of the image property is nil until you tell the object to load its image data. Listing 5-9 shows how your game tells an achievement description to load the image.

Listing 5-9  Loading a completed achievement image

[achievementDescription loadImageWithCompletionHandler:^(UIImage *image, NSError *error) {
    if (error == nil)
    {
        // Use the loaded image. The image property also holds the same image.
    }
  }];

The GKAchievementDescription class also provides two default images your game can use. The incompleteAchievementImage class method returns an image that should be used for any achievement that has not been completed. If your game is unable to load an image for a completed achievement (or you want to display an image while the custom image is loading), the placeholderCompletedAchievementImage class method provides a generic image for a completed achievement.