Requirements
Prerequisite knowledge
Previous experience creating games on iOS using Flash Builder or Flash Professional will help you make the most of this article. Additional required other products Game Kit AIR native extension by vitapoly or Game Kit AIR native extension trial by vitapoly
Required products
Flash Builder 4.6 Standard (Download trial)
Flash Builder (Download trial)
Adobe Animate CC
Adobe AIR
User level
Intermediate

With the Game Kit AIR native extension for iOS you can set up games built with Adobe AIR for turn-based Game Center multiplayer matches. This native extension also supports real-time matches as well as the leaderboard and achievements. The extension offers two APIs, including an easy-to-use high-level API and a low-level API that provides access to the entire native Game Kit framework, including all classes, properties, methods, delegates, and callback functions.
 
Creating real-time matches with this native extension is covered in the article Creating real-time multiplayer games on iOS with the Game Kit AIR native extension. If you haven’t already done so, please read that article to familiarize yourself with Game Kit AIR native extension basics.
 
Game Center allows up to 16 players to play a turn-based match together. Game Kit provides matchmaking functionalities to find other players along with all of the networking infrastructure and servers to store and retrieve match data. You can implement your game’s own logic on top of these capabilities without having to worry about setting up servers or managing players.
 
Although the Game Kit AIR native extension includes the entire Game Kit framework, this article focuses only on the high-level API so you can quickly jumpstart development of turn-based multiplayer games.
 

 
Understanding turn-based multiplayer matches

Each turn-based Game Center multiplayer match has a minimum of 2 and a maximum of 16 players or participants. There is a distinction between player and participant in this context. Participants are analogous to seats around a board game. Each seat can be empty or filled by a player. When the match starts, the number of seats or participants is set. The current participant then is the player who created the match. When a player finishes taking a turn, the play passes to the next seat or participant. If the seat is empty (the participant is null), Game Center tries to fill it using a matchmaking service. Once a seat has been filled, it cannot be unfilled. Instead, the player who leaves a seat forfeits or quits the match.
 
Unlike real-time matches, turn-based matches don’t have any direct connections between players. You don’t send data between players at all. Rather, you store and retrieve match data on Game Center servers. When a player finishes a turn, your game serializes the current state of the match in a ByteArray and stores it on the Game Center servers. When other players take their turn, your game retrieves the match data from the servers, reconstructs the match to its last state from the ByteArray, and presents this state to the players.
 
Game Kit provides the same matchmaking interface for turn-based matches as for real-time matches. Not only can you use it to start or find a match to join (see Figure 1), but you can also use it to manage all of your matches. You can invite friends or use auto-match to automatically find other players.
 
Figure 1. The native matchmaking interface to start or find a match.
Figure 1. The native matchmaking interface to start or find a match.
 
Figure 2 shows the matchmaking interface with a list of active matches. The user can select a match from this interface, and it is up to your game to display its own interface or take a turn depending on the state of the match. From this view, the player can also swipe an active match to quit, or swipe an ended match to remove it from the list.
 
Figure 2. The native matchmaking interface for managing matches.
Figure 2. The native matchmaking interface for managing matches.
 
Tapping the right arrow of a match presents a view showing more information about that match. Figure 3 shows this view with time information for the match, actions the user can take, and the list of players.
 
Figure 3. The native matchmaking interface with information for a match.
Figure 3. The native matchmaking interface with information for a match.
 
Before going further in this article, please follow the steps in the Setting up and Getting started with the Game Kit AIR native extension sections in the article Creating real-time multiplayer games on iOS with the Game Kit AIR native extension.
 

 
Initializing for turn-based matches

Like the Game Kit AIR native extension API for real-time matches, the high-level API for turn-based matches is exceptionally easy to use. The TurnBasedMatchesController class enables you to create and control these matches. The first step is to initialize it with init():
 
gamekit.turnBasedMatchesController.init();

 
Joining a match

With the Game Kit AIR native extension you can use either the native matchmaking interface or create a custom interface. Using the native interface is not as simple as it is for real-time matches, but you still listen for events and call one method to join a match. When matchmaker returns a match to your game, it is either a new match where the player is the first and current participant, or an existing match that satisfies the request and has the player as the current participant.  Follow these steps to join a match:
 
  1. Listen for the event that is triggered when matchmaking fails. This happens when there is an error connecting to Game Center or when Game Center cannot find a match that satisfies the request. You should display the error message and update your interface when this happens.
gamekit.turnBasedMatchesController.addEventListener(TurnBasedMatchesController.MATCH_MAKER_FAILED_EVENT, function(e:ErrorEvent):void { trace("MATCH_MAKER_FAILED_EVENT: " + e.errorID + ", " + e.text); });
  1. Listen for the event that is triggered when the player cancels the matchmaking process. You should update your interface accordingly when this happens.
gamekit.turnBasedMatchesController.addEventListener(TurnBasedMatchesController.MATCH_MAKER_CANCELLED_EVENT, function(e:Event):void { trace("MATCH_MAKER_CANCELLED_EVENT"); });
  1. Listen for the event that is triggered when it is the local player's turn in a match. This event can occur in three different situations:
  • The player just asked to join a match and the matchmaker created a new match. The MatchEvent class contains the match that was created or found. The TurnBasedMatchesController object also has it as the currentMatch property, which holds the latest match created or found. In this case, you should show a new game, and let the player take his or her turn.
  • The player is currently in the match the event is about, which means the TurnBasedMatchesController object's currentMatch property is this match. In this case, you should update the match and let the player take his or her turn.
  • The player is currently in another match. You should not show the match; instead maybe show a message to the player saying something like, "It's your turn for another match." Then give the player the option to switch to that match. If your game requires deep concentration, you may not even want to show the message and disturb the player, but add it to another screen where the player can see it later.
gamekit.turnBasedMatchesController.addEventListener(TurnBasedMatchesController.MY_TURN_EVENT, function(e:MatchEvent):void { trace("MY_TURN_EVENT: " + JSON.stringify(e)); if (e.turnBasedMatch.isNewMatch) startGame(); // local player just started the match, so show new game else if (e.turnBasedMatch.isCurrentMatch) takeTurn(); // it's the currently displaying match, so the local player take the turn else showMsg("It's your turn for another match: " + e.turnBasedMatch.matchID); });
  1. Listen for the event that is triggered when it is someone else’s turn in a match. If it is the current match, you would show the current game but not allow the player to take any action. If it is not the current match, you can simply ignore it.
gamekit.turnBasedMatchesController.addEventListener(TurnBasedMatchesController.NOT_MY_TURN_EVENT, function(e:MatchEvent):void { trace("NOT_MY_TURN_EVENT: " + JSON.stringify(e)); if (e.turnBasedMatch.isCurrentMatch) displayGame(); // not the player's turn, just show it else { // ignore it } });
  1. After adding the event listeners, you’re set to bring up the native matchmaking interface for the player to join a match. The function call is similar to the one used for real-time matches. The first two arguments are for minimum and maximum number of players in a match, respectively; for example:
// bring up native match making interface for a match with 2 to 12 players gamekit.turnBasedMatchesController.startMatch(2, 12);

 
Working with match data

Because there is no connection between players in a turn-based match, and the only method of communication between them is a ByteArray stored on Game Center servers, you will need to store enough information in the match data to recreate the match. Because there is a limit of 64KB for the match data, you have to carefully design the encoding strategy of your game's match data. Here are a few options:
 
  • You can encode only player actions, enabling you to recreate all steps leading to the current state of the game. This is suitable for games like chess, in which each player has a relatively small number of actions or moves. This also allows the players to replay each move and use that information to decide the next move.
  • You can encode only the current state of the match. You may want to do this if your match data for player moves is not under the size limit. However, this removes all context of the match. For turn-based matches, some players take a long time to make a move, and some players may be playing several matches at the same time. Without being able to see previous moves, it may be difficult for players to remember what happened previously, making your game harder to play. You should use this strategy as a last resort.
  • You can encode the current state of the match along with some recent player actions. This is the best of both worlds. You can immediately display the current state without having to recreate all steps from the beginning of the match. Plus, if players want to, they can go back a few steps to refresh their memory.
When a turn-based match starts, the match data is empty. Any participant can access it via the matchData property of TurnBasedMatch. However, only the current participant is allowed to change the match data by advancing the state of the match explained in the next section. You should implement a method—for example, deserializeMatchData()–to decode the ByteArray back into your own data structures.
 
var data:ByteArray = gamekit.turnBasedMatchesController.currentMatch.matchData; deserializeMatchData(data);
Similarly, you will need a function — for example, serializeMatchData() – that encodes your own data structures and returns a ByteArray.
 

 
Advancing the state of a match

When the current participant finishes a turn, you need to notify Game Center using advanceTurn() so it can pass the turn to the next participant; for example:
 
// serialize the entire match’s data into a ByteArray var data:ByteArray = serializeMatchData(); var message:String = getMatchMessage(); // optional gamekit.turnBasedMatchesController.currentMatch.advanceTurn(data, message);
The first argument is the match data to be stored on the Game Center servers. The second message is a human-readable string showing the current state of the match. This is displayed in the native matchmaking interface under each game. The advanceTurn() method also takes two more arguments, which are optional. The third argument is an array of participants. If not specified, the Game Kit AIR native extension automatically uses the list of participants starting from the current participant. The last argument is a timeout for the next participant to act. The default is a week. You might want to use a shorter time for your game. However, iOS 5 does not recognize the timeout period, and uses the default.
 

 
Handling player quitting

As the match progresses, a player may quit the match. Your interface should allow players to quit at any time. Your game needs to assign an outcome for the player. The outcome is a GKTurnBasedMatchOutcome constant, such as GKTurnBasedMatchOutcomeWon, GKTurnBasedMatchOutcomeLost, GKTurnBasedMatchOutcomeQuit, and so on. If the player quits during his or her turn, you must also update the match data and pass the turn to the next participant. Both cases require a single method call.
 
If the player quits during his or her turn, the code looks similar to when advancing a turn:
 
// update game and serialize the new match data into a ByteArray updateGameWhenPlayerQuits(); var data:ByteArray = serializeMatchData(); var message:String = getMatchMessage(); // optional var outcome:GKTurnBasedMatchOutcome = GKTurnBasedMatchOutcome.GKTurnBasedMatchOutcomeLost; gamekit.turnBasedMatchesController.currentMatch.quitDuringTurn(outcome, data, message);
If the player quits out of turn, the state of the game doesn’t change, and there are no changes to the match data. You only need to provide the outcome; for example:
 
var outcome:GKTurnBasedMatchOutcome = GKTurnBasedMatchOutcome.GKTurnBasedMatchOutcomeLost; gamekit.turnBasedMatchesController.currentMatch.quitOutOfTurn(outcome);
The native matchmaking interface also allows the player to quit a match. The Game Kit AIR native extension dispatches an event when this happens. You will need to respond to it by calling one of the above two methods; for example:
 
gamekit.turnBasedMatchesController.addEventListener(TurnBasedMatchesController.PLAYER_QUIT_EVENT, function(e:MatchEvent):void { trace("PLAYER_QUIT_EVENT: " + JSON.stringify(e)); // the player quit a match from match making interface // quit it with a lost outcome var outcome:GKTurnBasedMatchOutcome = GKTurnBasedMatchOutcome.GKTurnBasedMatchOutcomeLost; if (e.turnBasedMatch.isLocalPlayerTurn) { updateGameWhenPlayerQuits(); var data:ByteArray = serializeMatchData(); var message:String = getMatchMessage(); e.turnBasedMatch.quitDuringTurn(outcome, data, message); } else { e.turnBasedMatch.quitOutOfTurn(outcome); } });

 
Ending a match

When your game logic determines that a match has ended, you must assign outcomes to each player first, and then store the final match data. Without assigning outcomes to all players, ending the match has no effect. After a match ends, the native matchmaking interface still allows players to select that match. Your interface, however, should only display the final match state and not allow the players to make moves.
 
// make sure all participants have an outcome first gamekit.turnBasedMatchesController.currentMatch.participants[0].matchOutcome = GKTurnBasedMatchOutcome.GKTurnBasedMatchOutcomeWon; gamekit.turnBasedMatchesController.currentMatch.participants[1].matchOutcome = GKTurnBasedMatchOutcome.GKTurnBasedMatchOutcomeLost; … // serialize the final match state into a ByteArray var data:ByteArray = serializeFinalMatchData(); var message:String = getFinalMatchMessage(); // optional // end it gamekit.turnBasedMatchesController.currentMatch.endMatch(data, message);
You also need to listen for the event that is triggered when a match ends. If it is the current match, you would show the current end match state. Otherwise, you might want to show a message and give the player the option to see that match.
 
gamekit.turnBasedMatchesController.addEventListener(TurnBasedMatchesController.MATCH_ENDED_EVENT, function(e:MatchEvent):void { trace("MATCH_ENDED_EVENT: " + JSON.stringify(e)); if (e.turnBasedMatch.isCurrentMatch) displayGame(); // just show the end game else showMsg("Another match ended: " + e.turnBasedMatch.matchID); });

 
Where to go from here

You can now use the Flash platform together with the simple but powerful Game Kit AIR native extension to create turn-based Game Center multiplayer games. For a trial version of the native extension click here. For more API details, see the Game Kit AIR native extension documentation.
 
The namespaces com.vitapoly.nativeextensions.gamekit and com.vitapoly.nativeextensions.gamekit.realtime contain the high-level API. The low-level API, which includes the complete iOS Game Kit framework, is in the namespace com.vitapoly.nativeextensions.gamekit.ios.
 
We can’t wait to see your next turn-based (or real-time) multiplayer game!