Game Center Techniques, Part 1

Session 408 WWDC 2010

The developer preview of Game Center is enabled through new additions to the Game Kit framework in iOS 4. Dive into its rich API to get your Game Center application up and running. Learn the process for authenticating a player, managing achievements, and updating leaderboards. Learn from a variety of usage scenarios that can help you enrich your users' gaming experience.

Welcome.

I'm glad to see so many people interested in Game Center.

We're going to talk about some techniques on how you enable your Game Center game.

I'm guessing most of you are around for Mike's session that was just before.

But I'll go over a simple review of what Game Center is.

And then I'll go into some details about how you actually turn your games, your applications, into Game Center.

I'm Gabriel.

I'm part of the team.

We're here all week.

We have a lab.

Tomorrow we have another session in this room right after this where we'll be going into some more details.

So, Game Center.

Game Center is Apple's social gaming community.

It provides a whole bunch of features: friends, friends' lists.

It allows you to play with your friends; compete with your friends offline and online through leaderboards, achievements, and multiplayer gaming.

Peer-to-peer multiplayer gaming is really neat.

You'll see more of that later today.

I'm going to be focusing more on the first three of those slides bullet points: friends, leaderboards, achievements.

What you're going to learn today, Game Center API basics.

I'll go over a little bit about how our API is structured, how we can use this in some simple cases, how to authenticate, how you can remember.

You can use the player ID, which was mentioned before.

It's persistent.

You can use this to remember your players across different sessions.

You can store data.

I'll show you how you can report high scores, how you can access and show a leaderboard in game, how you can access the data from a leaderboard if you want to use custom UI, and achievements.

So I'll also show you how you can report achievements from your game; how you can show achievements in your game using the standard UI; how to query data from the achievements, your users' achievements; and, lastly, how to reset a user's achievements.

We've seen this slide.

It's an overview of what Game Center actually looks like.

There's a Game Center application.

Obviously, there's Your Games.

And the GameKit framework.

The GameKit framework is comprised of three different pieces.

There's parts that are only available from the Game Center application.

There's the piece in the middle which we're calling General, which is Authentication, Friends, Leaderboards, and Achievements.

That's what I'm going to be focusing on in this session.

And, lastly, there's the Multiplayer features which we'll go over in the next session.

So I'm going to be focusing on these four things today, and I'll be going through each of these sections in more detail.

To start off with, I want to take a little digression and talk about blocks.

Blocks are new in iOS4.

They were introduced in Snow Leopard.

A block is essentially an anonymous function.

In other languages, you might have heard them called closures or lambdas.

You can see here we have declared a block.

It's like any other C variable.

It follows the definition of a function pointer.

Instead of using an asterisks, it uses a caret.

One of the interesting things is that it actually captures enclosing variables.

So you'll notice in the block where we make reference to x, y, and z, z being passed in as a parameter, x and y are captured.

So when we get to this last line, it will print out 123, 456, 789.

A note that I want to make, we're only using static types here.

But you can also use objects.

And when you use objects, when you reference enclosing objects, it will automatically retain them.

And there's a particular note that I want to make about using instance variables because, when you reference an instance variable, you're making an implicit reference to self.

And so self will be retained inside the block.

If you're also retaining the block inside the object, then you have a retain loop.

And this is something that you need to be concerned about or you should watch for.

There's a lot more information on blocks.

There's another session.

I'll give you some details at the end of here where you can find out about blocks.

But we use blocks quite extensively.

And I'll show you exactly how we use blocks.

So this is not a real call but it's a template of how we're using blocks.

We use basically blocks as callbacks.

Most of the functions in GameKit are asynchronous, which means that an action is performed and you will be notified at the end once said action has completed.

And the way that we do that is we do that using blocks.

Here are some of the errors that you'll see.

GKErrorCancelled will happen if the user decides that they were going to cancel the event which you started.

CommunicationsFailure, if we have trouble; if the network is unresponsive.

InvalidCredentials, if the user that is using your game doesn't remember his user name and password.

NotAuthenticated, Mike mentioned authentication before.

I'll mention again right after here, all the features of Game Center require that your user is authenticated.

AuthenticationInProgress.

Since authentication is asynchronous, you might be tempted to try authentication if you're not already authenticated.

And you might actually try again.

If the user is in the middle of authentication and we receive a call to some function, we'll notify you.

ScoreNotSet, you'll see when we're dealing with leaderboards if you try to report a score that has no value.

ParentalControlsBlocked, something that you'll get back if there are restrictions.

We have built into the iOS there's restrictions that can be placed on which features are available.

And the last one is a MatchRequestInvalid.

So when you see matching, which we'll do in the next session, there are some parameters which you can set up with matches, and match has to has certain restrictions about how you can set up a match request.

And Nate will go over that.

So here we are.

We're at this list.

I'm going to focus on authentication, and this is the first class that I'm going to talk about.

This is GKLocalPlayer.

GKLocalPlayer represents the player on the device who's playing your game.

It has three pieces of information associated with that player.

He's got a player ID that is unique and persistent.

You can use this to store data about your players.

You can use this to extend the community, extend our feature set.

You have an alias, which is the nickname that Mike talked about.

It is unique but it's not persistent.

The user always has the ability to change their nickname.

And, then, underage flag to give you an indication that this user is underage and you might want to behave appropriately.

You also use GKLocalPlayer to perform authentication I'll show you that in just a moment and access your friends' list.

So here's the first piece of real code that we see to perform authentication.

Authentication is required to use any of the other features for any of the other features of Game Center.

So you need to do this as early as possible in your game.

It may present UI.

It will give the ability for the user to present their credentials if they haven't already been authenticated.

We share authentication across all games in the Game Center.

So it might be that you call authentication and your user is authenticated.

You'll notice this block here, indication of an error.

First thing that we need to do is we need to allocate the local player.

We use this we do this using a class method called localPlayer on GKLocalPlayer, and we store it for later use.

On this object, we now call authenticateWithCompletionHandler, and we give it a parameter, this block.

This block is going to get run sometime in the future.

It's a callback.

We're using them as callbacks instead of delegates.

We find this to be much easier to do and much easier to write.

So the actual block of the code gets run at a later time.

It might look quite linear as your reading it, but this code actually gets run in the future.

So here we have the response coming back from authentication with an error.

If there was not an error, that means your user is authenticated.

You should enable your GameKit features.

You should allow the user to use invites and leaderboards.

Otherwise, if there has been an error, you should turn off Game Center Enable Features.

Doesn't mean that you should stop your game.

Please continue your game.

Maybe the user has decided that they don't want to play online or they don't want to use Game Center and features.

And they've notified this they've told you this by canceling the operation.

But your game should continue on.

So this is what it will look like to your user.

If he's not already authenticated, he'll be given this opportunity to either sign in using an existing account that he's already created or to create a new account.

If he signs in using an existing account, it's the iTunes account.

And he's given a place to sign in.

If the user's already signed in, has already been authenticated through another application or having used your game earlier, then you'll see this welcome back message that comes in.

Then you can enable the buttons to do invites, matching, leaderboards, etc. Now I'm going to talk a little bit about friends, how you access your friends' list, what you can do with the friends' list.

And we hit our second class.

This one's called GKPlayer.

GKPlayer represents everybody else in the community who's not the user on your device.

We have two pieces of information that we have about this player.

We have a player ID, which we can see is persistent.

You can use it to keep track of data for those users, and you can keep track of those players over time.

The alias should be used in your game to display the user's name.

So, when you're in a match and you get matched with somebody, you should use the alias to show - hey, you're now playing with this person.

This is who's blue, this is who's red, that kind of thing.

This is how you get your friends' list.

The first thing that we need to do is, again, start with localPlayer.

If you've already done this, you could use the same object.

We call loadFriendsWithCompletionHandler.

This block is a little bit different than the last one that we saw, because it has two parameters in it now: one for the array of friends, which gets returned to you; and one for an error.

You'll notice in the block where I'm actually handling both cases, if, for instance, there is a communications failure, we might have that friends' list cached.

So we'll return that to you, even though you can't access the server.

It might be slightly out of date, but it's the best information that you have, as well as also return you an indication that there was an error.

You should handle both cases.

So, in this case, the friends' list that's returned to you is an array of GKPlayers.

[ Pause ]

What to do with the friends' list.

I've already told you that you can associate custom data on your own servers with the player IDs.

And here are some ideas that you can do, you can use to extend the Game Center functionality.

Virtual goods.

If you want to gift things to other friends, you can use the friends' list to find out who your friends are.

You can give them things.

This is outside of the features that we provide you.

Cross-session messaging.

If you have virtual worlds, you have friends, you can leave messages for other player IDs on your servers.

When the user starts your game and they log in, they can have this information to them.

You can use a social graph as part of your gameplay.

You can have your users visit each other.

This doesn't have to happen online.

And you can use the connections to enhance the character's virtual abilities.

[ Pause ]

Now I'm going to go into leaderboards.

A little bit longer than the last section but still quite easy to do.

So there are three classes now that we need to talk about for leaderboards.

GKScore is the actual score.

A GKLeaderboard is a class that you can use to access the data inside the leaderboard.

And there's a GKLeaderboardViewController.

The view controller is the standard UI that we provide to you so to make it very easy for you to show leaderboards inside your game in a few lines of code.

I'll go through each of the different things that you can do.

First of all, what is a leaderboard?

There's a screenshot of a leaderboard.

A leaderboard's a list of high scores.

There's filters Mike alluded to those friends, all players, different time possibilities.

We're also providing categories.

Categories are a way to provide multiple leaderboards.

This is something that a lot of people had asked for.

So you can separate your leaderboards into easy, medium, and hard.

You can have different tracks or different levels.

In addition to that, you can have one global leaderboard, where any score to any of the categories that you provide, you'll have one leaderboard which shows you overall scores.

Steps to using leaderboards.

You have to set it up first.

So there's a server-side setup component.

There's reporting scores.

There's showing the scores using the standard UI, showing the leaderboards.

And there's querying leaderboard data.

You would want to do this if you want to build your own custom UI so it's a little bit more integrated into your game and the style.

First I'll talk about server-side setup.

This is done in iTunes Connect.

You need to enable leaderboards for your game, and then you need to set them up.

And a leaderboard consists of a few different properties that you set in iTunes Connect.

There's a leaderboard identifier, which is a string.

There's a sort order, ascending or descending.

And this is the order that shows up in the leaderboard.

So, if I have a descending, it means that higher scores are better because, as you go down the list, they get lower.

Ascending, the other way around.

So if you have a golf game, for instance, lower scores would be better.

And you can localize this in many different languages.

There's a format for your score: integer values or time values, how you want to represent the score; and a suffix in many languages.

More languages are better.

It helps sell your game.

More people are interested.

More people will be able to play it.

This is a screenshot of iTunes Connect.

You can see there's a place for a leaderboard ID.

This is an ascending one.

You can set the sorting.

And all the languages are below.

In this case, it's only in English and Spanish using integer values for gold coins.

But I suggest you use as many languages as you possibly can.

This will show based upon the locale of the user who's using the game on their device.

[ Pause ]

I want to talk a little bit about formatting because it's interesting.

I have a few different selected formatters here and some examples, the first one being an integer value, 123,456.

It will format it with a comma.

Depending upon your locale, we have different formats that you can choose for an integer value.

Time values, I've shown two here.

The first one is in minutes.

So the value that you submit would be 792 as an integer, but it's represented it's interpreted as the number of minutes.

So, when it's presented, it actually shows up as 13 hours and 12 minutes.

Similarly, when you have a second value, you can choose to have your value represented as seconds.

So you have here 3,723 seconds, which shows up as an hour 2 minutes and 3 seconds.

And money, 1,042 shows up with a dollar sign and comma, as appropriate.

There's about 17 formatters in all.

You can see these on iTunes Connect when you choose to do the localization.

So reporting a score.

It's quite easy.

You allocate a GKScore.

You set a value, you set a category if you're using multiple leaderboards, and you report the score.

A note: If you're offline and there's an error in communicating with the server, it's your responsibility to save that score and attempt to report it at a later time when the network conditions are more favorable.

We make it easy for you.

I'll show you that when I get to the code.

These are the properties of a GKScore.

Some of them are read only.

They'll be returned to you when you ask for leaderboard data.

A value is the integer value of the score.

A formatted value is a string formatted by the server based upon the properties that you've set in iTunes Connect, how that score is interpreted, and the optional language suffix.

Category, if you're using multiple leaderboards.

A date, which is the date, the time that the score was created.

The player who actually created that score.

And a rank where it fits on the leaderboard.

This is how you report a score.

You start off allocating a player score allocating a GKScore.

Here we call it myPlayerScore.

We set a value for the score, 12345.

And we report the score using this function, reportScoreWithCompletionHandler.

There's no result returned, just an indication of the error.

If there was an error, that would make it easy for you.

It's an encodable object so you can use NSKeyedArchiver.

So, to serialize this, you can store it offline so that you can try again either when the next time your user plays the game or later in the play session.

If there was no error, you're done.

The score was submitted successfully.

You can see it.

Which brings me to the next point: How do you show these to the user, right?

You have a list of scores.

This is using the leaderboard standard UI.

There's a class called GKLeaderboardViewController.

It's a modal view controller.

There's a button at the top right which says done so that the user can indicate, I'm finished looking at the leaderboard.

And so there's a delegate method which you have to implement.

I'll start off with that.

leaderboardDidPressDismiss.

All we're doing is we're dismissing the modal view controller, which will take it off the screen.

This will appear on top of your view.

So, to show the leaderboard, we've created this function called showLeaderboard.

The first thing it does is it allocates GKLeaderboardViewController.

It initializes it with some filters.

In this case, we're interested in all the scores over the last week for my friends.

And we set the delegate to ourselves so that we'll know when they're finished, and we present the we present the view controller.

I think that's fairly simple.

We also give you the ability if you want to create your own custom UI to query the leaderboard.

To do this, you use the GKLeaderboard class.

You set the scopes similarly to how we just how I just showed you.

You set a range of ranks that you're interested in.

As Mike said, you don't want to load the whole leaderboard.

There might be thousands or millions of entries.

It's an asynchronous operation that communicates with our server.

It could take a fair amount of time, especially if the list is really long; and that's a poor user experience.

Lastly, you can set a category if you're using multiple leaderboards; and you can query the data.

It returns an array of GKScores.

We've seen this before.

But just to reiterate, these are the ones that you're interested in.

The player who scored that score, you can get their player ID and their alias; the rank where it fits in the list; the value, the formatted value which comes from the iTunes Connect properties; and the date.

So here we actually see some code, how to query the leaderboard.

So we start off with allocating a GKLeaderboard object.

We set the properties that we're interested in again, friends only and the scope is week, so all the scores over the last week.

And I'm just going to take a look at the top of the leaderboard.

I'm using a range here.

Normally, ranges start with index values; but leaderboard values start with 1.

So, in this case, we're looking at 25 values starting at the value 1.

So it will show you the top 25 list.

And then we call loadScoresWithCompletionHandler.

It returns to you an NSArray of scores, GKScores, to be precise; and an error.

Again, we might pass you back some cached data in the case of a communications error.

So you should be prepared for both those values to be returned to you.

If there's scores using in this case, I'm just doing a fast enumeration across the list, and you'll want to do something with it to show it in your own custom UI.

If there's an error, you might want to indicate that to the user.

You might want to try again, depending on what the error is.

That's querying a leaderboard.

So I have a few screenshots of what the leaderboard looks like.

We provide both landscape and portrait views.

So this is what it looks like in landscape.

You can see here there's two sections.

There's a friends section, an all player section, and, across the top, you have the ability to change the filters.

This is would what it would look like in your game.

If you choose to do a custom UI, you could do something like this.

This is from Onward.

It's a game that we've been using inside for testing.

And here you can see they've done something slightly different.

What they do is they actually show two different time scopes at once, scores over the last week and all time scores.

It's chosen not to separate that between friends but, as a developer, you can do whatever you want.

And I'm really interested to see what you do.

Landscape, it looks slightly different but pretty much the same.

You have that two sections, one for friends and one for all players and the filters across the top.

And, likewise, you can easily do custom.

This comes from Quest.

That's leaderboards.

I think I'm going a little fast.

Here we go.

We're going to talk about achievements.

Achievements, coming soon.

Not all of it is available.

Particularly what's missing is the server-side setup.

The APIs are there if you want to test your code.

There are three classes involved: GKAchievementDescription is a list of all the achievements for your game across all users.

There's a GKAchievement, which is the list of achievements recorded by the user, the GKLocalUser in this case.

And GKAchievementViewController, which is the standard UI.

So what is an achievement?

An achievement is something of significance, something your player's done inside your game that you want to record, maybe reward them for.

Examples: finishing a level really fast; finding all the treasure in a dungeon.

Come into our labs tomorrow.

Please come to our labs tomorrow.

It increases the engagement.

Players will want to come and complete all the achievements.

They'll want to get all the achievements that you have possible.

They'll want to finish your game.

They want to get to 100 percent complete.

It also encourages competition.

Inside the Game Center application, you've seen you can compare the achievements between your friends.

You want to say to your friends, I got this achievement.

You should go get this achievement.

Helps engage your game.

Helps keep your game last.

People want to play the game.

These are the steps involved in using achievements.

There's a server-side setup.

I'll go over that a little bit.

You can report achievements.

You can access the descriptions for your achievements.

You can query your achievements for custom UIs.

You can show your achievements to your users.

There's a standard UI.

I'll show you that.

And there's the ability for you to create your own custom UI.

And, lastly, there's a way to reset the achievements.

Somebody in the earlier session asked, "What happens if I add more achievements?"

If you add more achievements and somebody's already finished your game and they want to go back and play it again, this is a perfect opportunity for them to reset their achievements.

The server-side setup is done in iTunes Connect.

You can enable achievements in your game.

And you describe each of your achievements.

This is what you need to do for each achievement.

There's an achievement ID.

It must be unique within your game.

We suggest using an inverse DNS, something like com.myCompany.myGame.achievementX.

There's a title for the achievement.

There are two descriptions for the achievement, one that shows up before the user's achieved it and one that shows up after the user's achieved it.

There's a point value associated with each achievement.

There's a hidden flag I'll talk about that in a minute and an icon.

The hidden flag, as mentioned before, will allow you to set some of your achievements as hidden.

So they might be secret achievements that you want your users to discover by playing your game.

They might be achievements that you don't want them to see because they haven't bought the rest of your game if you have some in-app purchase which has extra content.

You can unhide those achievements once the user has purchased that content.

But it's fairly flexible.

This is what you'll see inside the code, right?

It's similar to what you put into iTunes Connect, but it's a list of each achievement has a description.

And these are the properties that you'll see from the framework.

So there's a unique ID we talked about is a string, preferably inverse DNS.

You'll use this to reference your achievements, either reporting them or visualizing them.

There's a title.

There's an icon.

The icon only shows up once the user's achieved it.

You'll see in this case there's a few different cases, the question mark where he doesn't have the achievement, a partial achievement.

There's two descriptions, again, one which you'll see before you've achieved it and one which you'll see after you've achieved it.

There's point values associated with each one, and there's a flag to hide the achievement.

[ Pause ]

A GKAchievement, this is the record of the achievements that are made by the user, the GKLocalPlayer.

The unique ID is the same.

Percentage complete, how far along that achievement has the user progressed; the hidden flag; a completed flag, which is set once the user has completed the achievement; the date that this record has been reported; and the number of points that the user's been given for this achievement.

So first we'll talk about reporting an achievement.

Similarly to leaderboards, it's very easy to do.

We use GKAchievement using the achievement ID to identify it.

We set a percentComplete and we report it.

As the developer, you're responsible for retrying if there's communications failure.

The object GKAchievement is a codable object.

It's quite simple to store for later use.

And this is actually how we do it.

We start allocating a GKAchievement.

We initialize it with the identifier.

More explicitly in this case, we see com.myCompany.myGame.achievementX.

We set a percentComplete.

So I'm 50 percent done; I'm halfway through this achievement.

And we report it.

Again, this is a block.

The body of the block, although it looks like it's linear, it's actually a callback.

There's an error if there's an indication of an error.

If there was an error, in this case, what you want to do is you want to archive the achievement so that you can reuse it and you can try to report it.

If there was no error, the achievement was submitted successfully and the hidden flag will be unset.

If you hide an achievement and the user reports the achievement, that means they found out about it.

And so we unhide it.

[ Pause ]

Achievements Standard UI.

This is a GKAchievementViewController.

It's a modal view controller.

It requires a delegate.

There's a done button.

This is what the delegate look like, achievementsDidPressDismiss.

In this case, we just dismissed the modal view controller.

We have a function here called showAchievements.

So the first thing that we do is we allocate the GKAchievement controller.

There's no properties that we need to initialize it with.

We need to set the delegate to ourself and then we show the delegate we show the modal view controller.

It's quite simple.

[ Pause ]

Getting Achievements Descriptions.

So now I'll talk a little bit about achievements and how you would want to create your own custom achievements or custom UI for achievements, rather.

Achievement descriptions, as I mentioned before, is basically the list of achievements for your game.

So, in this case, they're the same for all the users of the game.

I want to start off by loading up this achievements description table so that I can use it later on in my game.

It's a dictionary.

We start off by loading the achievements.

It's a class method on GKAchievementDescription, loadAchievementDescriptionsWithCompletionHandler.

And it returns to us in the form of a block callback an array of descriptions and an indication of the error.

In this case, what we're doing is I'm just loading up this description table.

Now, I'm using the IDs as keys into this dictionary so I can refer to them.

When I ask for the achievements, you'll see that the achievements come back with an achievement ID.

The achievement ID is the same ID that we set in iTunes Connect.

It's the same ID that's used here, so it will allow me to cross-reference the achievements that my user has used with the achievement descriptions.

And I can use both the data in the UI that I'm creating.

If there's an error, you'll want to indicate this to your user somehow.

You'll want to try again, something like that.

Once we have those descriptions in the description table, then we can actually look at the achievements for that user.

You'll notice that the first line is the same first line as was on the last screen.

In fact, it's just a repeat; but it's the same object.

It's a dictionary that I loaded with the descriptions so that I can reference that in the body of the block coming up.

So here we see we're using now a GKAchievement as opposed to a GKAchievementDescription, because I want to look at the user's achievements.

So I start off with a loadAchievementsWithCompletionHandler, and it returns to me a list of achievements and an error.

This will happen in the future.

It's an asynchronous call.

All of the calls we've seen today are asynchronous.

They all look quite similar.

If there are achievements, I'm going to loop over the achievements.

I'm using fast enumeration here.

You'll see first I pull out the description from that dictionary.

And, then, in this case, I'm just printing out a log.

What you'll want to do is create a UI object, an entry in a table view or something like that where I'm using some of the values from both the description and the achievement.

So here I'd be printing out how many points out of how many maximum points and I'm printing out the title.

There's lots of other properties there.

There's a percent complete.

There's descriptions.

You might want to hide them yourself for the ones that are hidden.

You can do lots of interesting things.

[ Pause ]

And, lastly, we get to resetting user achievements.

Again, I mentioned that you want to give your users the ability to start over.

Maybe they missed something.

They missed something on level 2 and, in order to get back to that, they want to reset their achievements.

There's a simple method here called resetAchievementsWithCompletionHandler with a block.

You see it like this.

The block returns an error, and an indication of whether or not the list was actually reset.

If there was an error, you might want to try again or indicate to your user in some fashion that there was an error, that their achievements were not actually reset.

Otherwise, there's nothing for us to do.

Thank you.

I'm going to show you a few screenshots now.

This is the standard UI.

You'll notice various different styles.

There's one at the top which is 70 percent complete.

There's one in the middle, Sailor's Mark, which you have not achieved point values for each one.

And also we can see what it might look like inside a game.

This comes from Quest.

They've done a pretty job.

You'll see the Demon Slayer one, which is partially achieved.

You'll see what they did with the hidden one is they marked it as secret.

So, in summary, what we talked about, blocks.

Blocks are an important part of GameKit, Game Center's framework.

Most of the calls that you've seen are asynchronous, which means you make the call so you ask for a leaderboard.

You say get leaderboards with leader scores with completion handler and your block is run asynchronously.

So we'll communicate with the server and we'll report back to you when we have that data.

So it may happen at any time, and you need to be aware of this.

Authentication is really important.

All the other features rely on your user being authenticated, so you should do this early in your game.

However, your game should continue if the user has chosen not to authenticate or can't authenticate.

You just won't be able to use any of the Game Center features.

We've seen how to report a high score.

We've seen how to access the leaderboard data, either in the form of a standard UI or the data itself so that you can create your own custom UI.

We've seen, similarly, for achievements, a little bit more detailed in achievements.

But we've seen how to record achievements; we've seen how you can use the standard UI for achievements; how you can access the data from the achievements so you can create your own standard UIs; and, lastly, how to reset achievements.

A note about achievements.

I mentioned previously, the API is in the developer seats, but you won't actually have the ability to set up your achievement lists until later this year when they add that functionality to iTunes Connect.

I'm sorry I didn't take more time.

But if you want more information, Allan is available.

Here's his email address.

There's lots more documentation both on blocks and on Game Center at the iPhone Dev Center.

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US