Monday, 19 March 2012

Adding Game Center achievements to Balls Abound

Based on my love of having a checklist to complete in a game, I decided that Balls Abound needed achievements before I could call it finished. The hope, of course, is that achievements will keep people who need a goal playing the game. I'm already supporting Game Center for leaderboards, this should be simple...

Defining the Achievements

First thing to do was figure out what achievements to make available. In a pinch I created 3 or 4 achievements for getting various levels of score in the game. Next, I put a lot of effort into stereo positioned sound for the game, so to encourage people to experience it I added an achievement for plugging in headphones. I also added a couple of hidden achievements for the "easter eggs" in the game. After that things got tougher, and I ended up just mucking around with the game and stumbling on a few ideas I liked. The full list of achievements I decided upon is as follows:


Karmapurchased the full gameYes100

Touchdownused finger on splash screenYes50

Poke the crabpoked the crab 100 timesYes50

Century Clubearned 100 points in a gameNo25

High Roller Clubearned 10,000 points in a gameNo50

100 Grand Clubearned 100,000 points in a gameNo75

Mile High Clubearned 1,000,000 points in a gameNo100

Palindromelit the signs in order

Stuck on youcleared three fields of coconuts,
hitting only the letter 'A'

Double Hitterturned two signs on in one bounce,
two signs off in one bounce, and
turned one on and one off in one bounce,
all in one game

Audiophileplugged in headphonesNo100

Nauseacleared two fields of coconuts,
while playing upside down

Multiball Masterearned 1,000,000 in games
with each of the types of ball

Luckily I had created the pelican icons a while back and finally have a use for them.

In total Balls Abound has 10 visible achievements and 3 hidden ones, for players to work towards. iTunes Connect is used to define the achievements in Game Center. Use iTunes Connect to add localizations, point values, icons, and other achievement properties. The identifiers you assign to the achievements defined in iTunes Connect are referenced from your code when obtaining GKAchievement objects, which are used to communicate progress to Game Center.

Tracking the Achievements

I am rushing to get this game finished, and I am the only person touching the codebase. With this in mind, my main goal is not the most beautiful design right now, I just need things to work.

I am already using Core Data to track some of the user settings in the game (muted, etc..) so I decided to model a new class in XCode to track achievements progress. Easy enough, now I have a new managed object that holds floats that represent the progress towards each of the achievements; they mirror the data stored in GameKit's GKAchievement objects. Another require for storing achievement data is, multiple Game Center users might play on the same device. So you need to track achievement progress on a per user basis. Adding a userId attribute to the UserAchievements managed object allows tracking achievements on a per user basis, creating one UserAchievements object for each userId. I create or attach the UserAchievements for the current user to my game settings whenever Game Center authentication is completed. I also use this as an opportunity to load the users current achievement status from Game Center, and reconcile any discrepancies between local and remote progress (using the rule, higher progress always wins).

Now that there is a data model capable of storing user achievement progress, three more things are required.
  1. an AchievementTracker that will monitor achievements and update the UserAchievements data
  2. an AchievementBoard UI to listen for UserAchievements changes and put notifications on screen
  3. the ScoreKeeper must listen for UserAchievements changes and report updates to Game Center
The AchievementTracker class is bound to all the points needed to listen on when detecting achievement progress. The AchievementTracker has the ability to track game state, coconut hits, sign hits, and accelerometer readings. This allows the AchievementTracker to decide when progress towards an achievement is made and update the UserSettings accordingly.

Listening for the Achievements

The ScoreKeeper and AchievementBoard both listen for achievement updates through KVO on the UserAchievements object. In order to achieve a reasonably performant option, I treat achievement progress internally as an int, rather than a double, which helps limit the number of updates that I have to process. Updates to achievement progress are relatively expensive, they trigger a KVO transaction which is followed by a managed context save and possibly network traffic through GameKit reporting through GKAchievement objects.

Displaying the Achievements

I decided to create my own UI for displaying achievements, the AchievementBoard. This was largely due to the fact that Apple only provide functionality for displaying achievements in iOS 5.0+ and I wanted to support iOS 4.2+.

I wanted a UI that would animate it's entrance and exit, was capable of handling multiple simultaneous achievement updates, and could display at top and bottom of the screen. The AchievementBoard holds a queue of achievement updates. If progress towards an achievement is made, it will either append the update into the AchievementBoard's queue, or replace the progress in an existing update for the same achievement. The AchievementBoard coordinates the onscreen display of achievement updates that are in it's queue. This system allows multiple achievements to be acted on simultaneously, with no notifications being ignored.

Coming up with graphics that tied into the game took a number of iterations of producing graphics and redeploying the game to test their visuals.  I eventually came up with the following, which I think works well.

The Final Touches

With achievements being monitored, updates being communicated to both Game Center and the display, there is just one more task before achievements can be considered fully integrated. There needs to be a button on the high score view so people can view their achievements in Game Center. Ideally I would create an in game view for achievements and leaderboards, but that is out of scope for now. So the quick solution is a simple button that opens a default GKAchievementViewController for displaying achievements. This leads to launching Inkscape and drawing a new icon for viewing achievements and updating the icon that already existed for viewing leaderboards. This in turn means running TexturePacker again, and finall PVRTextTool to generate my pvr with mipmaps. Anyway, achievements are finally present in Balls Abound.

Everything for achievements is plugged in and working. Except I'm suddenly down from a nice smooth 60fps to ~40fps in many parts of the game. I'll save the details for another post, but a day or two of work later and I am back to 60fps consistently (my solution involved clipping with glScissor and a hack to CCLabelBMFont). If only everything in life were so straightforward...

No comments:

Post a Comment