Turn-Based Multiplayer

Warning: This API requires IOS 8.0 or higher.

This article will demonstrate how you can implement Turn-Based matches to your game. Originally article was copied from the Turn-Based Matches Game Center Programming Guide. The defenses with apple original article and this one is:

  • All code snippets are Unity C#. 
  • I've added more explanation to the API parts which I guess wasn't widely covered in original.
  • Article will contain much more API methods descriptions.

In a turn-based match, the players of the match do not need to be simultaneously connected to Game Center to play a match together. Instead, the game uses a store-and-forward approach; one player takes a turn before passing the next play to another player. The players continue to take turns until the match ends with only one player able to make changes to the game at a time.

Turn-based matches have many useful characteristics that make them great for implementing board games and other similar styles of games; for example:

  • A player can participate in multiple matches simultaneously. A game loads whichever match the player is interested in viewing or advancing.
  • Players must connect to Game Center only to take a turn.
  • A match can be created with less than a full complement of players, even a single player. Other players are added as needed.

Checklist for Implementing a Turn-Based Match

To add turn-based support to your game, you write code to handle the following activities:

Every Match Has a List of Participants

Think of a turn-based match on Game Center as a board game in the middle of a table. Around the table are empty seats, waiting to be filled by potential players. The number of players is set when the match is first created and never changes.

When a match begins, only some of the seats may be filled by players. The other seats may be reserved for specific players or to be filled by Game Center’s matching service. For example, consider Figure 10-1. Mary has created a new match with four seats. Because Mary created the match, she performs the first turn and then play passes to another player.

Figure 10-1  Mary starts a new match

At this point, Game Center sees that this match needs a new player to continue play. When Bob searches for a match, Game Center sees that Bob wants to play the same game as Marry and assigns Bob as a new player of Mary’s match. Because this game was waiting on a new player to continue the match, it is now Bob’s turn.

Game Center always tracks the players of a match in a specific order. That order never changes for the duration of the match, regardless of which device your game is running on. In this example, if Mary is in the first seat (slot), she stays in the first seat for the entire game.

Every match has the concept of the current player. The current player changes throughout the match and represents the player whose turn it is to act. When that player finishes taking a turn, your game chooses another player to act. That player is notified that it is now his or her turn and becomes the current player.

The Match Data Represents the State of the Match

Your game sends to Game Center match data that describes the state of a match. Game Center sets a limit on the size of this match data but otherwise does not care about its contents. Your game should store whatever data is necessary to preserve the match state.

At any time, a player in the match can launch your game to view the match. When a player launches the game, your game loads the match data, interprets it, and displays it to the player.

Figure 10-2  The current player wants to view the match

Only the current player is allowed to change the match data. Provide the current player with a user interface that allows them to take actions in the game. As the player takes actions, your game updates the match data and transmits it back to Game Center.

Your Game Decides the Rules of Play

When you design a turn-based game, you create the rules that dictate play. You decide exactly what actions a player is allowed to take and when play passes to another player. When play passes to another player, you decide which player plays next.

As an example, consider chess. The player with the white pieces moves first. After white moves, play passes to the player controlling the black pieces. After black makes a move, play passes back to white. The players alternate turns until the match ends in either a checkmate or a stalemate. The rules dictate that play alternates and each player makes a single move each time.

The rules that chess uses for players are very consistent, but Game Center allows you to design more flexible games. For example, each time a player takes a turn, you can show them a different user interface screen and present the player with different options for play. A player’s turn can be as simple as making a single decision or as complex as choosing what to build, deciding where to send resources, and engaging in combat with another player. Your game tracks the moves a player makes and the moves they are allowed to make as part of your match data.

Save the Match Data Whenever Important Events Occur

The current player’s device can save the data to Game Center immediately whenever any action is taken, without transferring control to another player. You should save match data whenever anything interesting happens. Consider the following list as critical places where data should be sent to Game Center:

  • The action should be immediately visible to other players if those players choose to look at the match.
  • The action should be irrevocable, such as when the action reveals information previously hidden from the player or generates a randomized result that needs to be persistent.
  • The action changes the current player.

Game Center Imposes Limits on the Match

There are two of the important limits you need to consider when designing your game.

  • Number of Players: 16
  • Size of Match Data: 64k

Joining a New Match

For players, a new match begins when the player joins a match. As with other forms of matchmaking, your game starts the process by calling a  GameCenter_TBM.Instance.FindMatch(...) or GameCenter_TBM.Instance.FindMatchWithNativeUI(...) method. A match request does not guarantee that a new match is going to be created, it may also match the player into an existing match that has an empty position as the current player.

You can choose to display a standard user interface provided by Game Center or implement your own custom user interface. Regardless of which technique you use, the match returned to your game always has the local player as the current player expected to take a turn.

Defining Player Group and Attributes  

Game Center’s default behavior is to automatically match any player waiting to play your game into any match that needs more players. This has the advantage of matching players quickly into matches, but it also means that players can be added to matches that they are not interested in playing. In that case, you can allow the players to define the kind of matches they want to play, and then match them only with like-minded players. This is accomplished with a player group.

A player group is defined by an unsigned 32-bit integer. Without setting Player Group property,  the player can be matched into any waiting match. When you set the Player Group property to a nonzero number, then the player is matched only with players whose match requests share the same player group number. Typically, if your game uses player groups, it provides a custom interface that allows the player to pick the precise options he or she is interested in. It takes the choices of the player and combines them to form a player group number.

Here are some examples of how you can partition a list of players:

  • Separate players by skill level.
  • Separate players by the set of rules used to adjudicate the match.
  • Separate players based on the map the match is played on.

Although it is up to you to determine exactly how many player groups you want to create, don’t create groups just to create them. Creating many smaller player groups can result in every player waiting for a long time to play a match. Instead, create large groups that players can identify with

Player Group should be set before you starting the match search, the code snippet bellow shows how to set player group property.

int GroupId = 5;


 If the Player Attributes value is nonzero, then automatching uses the value as a mask that restricts the role the player can play in the group. Automatching with player attributes matches new players into the game so that the bitwise OR of the masks of all the players in the resulting match equals 0xFFFFFFFF.

If you want to use Player Attributes property in your game it  should be set before you starting the match search, the code snippet bellow shows how to set player attributes property.

int ROLE_WIZARD = 0x4; // 100 in binary
GameCenter_TBM​.instance.SetPlayerAttributes (ROLE_WIZARD);

Showing the Standard Matchmaking User Interface

Listing bellow shows a typical implementation for the Standard Matchmaking User Interface

int minPlayers = 2;
int maxPlayers = 2;

//Optionally you can provide and invitation message
string invitationMessage = "Come play with me, bro.";

//Optinally you can predefine invited friends list to the match
//Teh code bellow assumes that player has atleast one friend, and you already loaded the friend list

//so we can send an invite to the first player in the firendlist
string[] playersToInvite  = new string[] { GameCenterManager.FriendsList[0] };

GameCenter_TBM.Instance.FindMatchWithNativeUI(minPlayers, maxPlayers, invitationMessage, playersToInvite);


For matchmaking, you need to handle Action<GK_TBM_MatchInitResult> ActionMatchFound 

The code snippet bellow, shows the basic implementation

GameCenter_TBM.ActionMatchFound += ActionMatchFound;

void ActionMatchFound (GK_TBM_MatchInitResult result) {
	GameCenter_TBM.ActionMatchFound -= ActionMatchFound;
	Debug.Log("ActionMatchFound IsSucceeded: " + result.IsSucceeded);
	if(result.IsFailed) {
	} else {

Implementing a Custom Match Interface

To create your own custom match interface, you typically use methods on the GameCenter_TBM class. Listing bellow shows a trivial implementation of this technique. It creates a new match request and uses it to find a new match. (Typically, if your game was implementing a custom match interface, it might set other properties of the request object, such as the playersToInvite property.) If a match is found, it transitions to the gameplay screen.

int minPlayers = 4;
int maxPlayers = 16;

GameCenter_TBM.Instance.FindMatch(minPlayers, maxPlayers;
GameCenter_TBM.ActionMatchFound += ActionMatchFound;

void ActionMatchFound (GK_TBM_MatchInitResult result) {
	GameCenter_TBM.ActionMatchFound -= ActionMatchFound;
	Debug.Log("ActionMatchFound IsSucceeded: " + result.IsSucceeded);
	if(result.IsFailed) {
	} else {

One common scenario is supported automatically by Game Center. When a match ends, you can call  Rematch  method to create a new match with the same participants


Working with Existing Matches

If you display the standard matchmaking user interface, then the player sees existing matches as well. The player can choose an existing match already in progress and perform other common tasks. If the player picks an existing match, the  Action<GK_TBM_MatchInitResult> ActionMatchFound   is called, exactly as it was called when a new match was created. Note that in this case, the player may not be the current player.

A player can choose to resign from a match from within the standard user interface. Your delegate must implement a Action<GK_TBM_MatchQuitResultActionMatchQuit  delegate to handle a player resignation. Your game must set a match outcome for the resigning player, and if necessary, choose another player to act in the match.

If you implemented a custom matchmaking user interface, you need to provide equivalent functionality that allows a player to manage the list of matches. Listing bellow shows how to retrieve a list of matches that the local player is participating in.

GameCenter_TBM.ActionMatchesInfoLoaded += ActionMatchesResultLoaded;

public void ActionMatchesResultLoaded (GK_TBM_LoadMatchesResult res) {
	GameCenter_TBM.ActionMatchesInfoLoaded -= ActionMatchesResultLoaded;
	Debug.Log("ActionMatchesResultLoaded: " + res.IsSucceeded);

	if(res.IsFailed) {

	if(res.LoadedMatches.Count == 0) {

	foreach(KeyValuePair<string, GK_TBM_Match> pair in res.LoadedMatches) {
		GK_TBM_Match m = pair.Value;

Retrieving Information About a Match

When a player decides to view a match, your game reads the properties of the match to determine the current state of the match. The match is represented as the GK_TBM_Match object.


Working with Match Data

When a match is first created, the match data is empty. But once the match begins and players start taking turns, your game is expected to provide match data that makes sense for your game. The match data is a byte[] array and its size is limited, so you need to be creative and encode your game’s data so that it fills as little space as possible.

In general, the match data needs to store enough information so that your game can display the current state of the match. If the player is the current player, then the match data should also store enough data so that your game knows what kind of turn the player may take. Here are a few possible strategies you can follow when designing your match data format:

  • Encode only player actions: In this design, your match data simply consists of the moves made by the players. For example, in chess, you know that white goes first, moves always alternate, and a piece moves from one board position to another. This makes it easy for you to encode each move as the starting and ending position of the piece moved. The rest of the data (who made the moves) can be completely inferred. When your game loads the match data, it quickly replays the moves to generate the current board position.
  • This strategy works best for games with a small number of possible kinds of actions and a small number of moves per match. Also, with this model, it is very possible for your game to replay the moves in its user interface, allowing players to see exactly what moves other opponents made to get the board into the new state. Showing a player these moves makes it very easy for a player to understand how the game got to the current state.
  • Encode only the current state of the match: For very complex games, the actual state required to encode the game could be very large. In this case, you may need to encode the current state of the match without worrying about the actions that generated that match data. This is particularly true for very complex games where the list of moves might grow too large to fit in the available storage space.
  • This strategy is recommended as a last resort. Players lose all context of what happened on previous turns of the match. For games with long timeouts between turns, players may grow bored or frustrated if they cannot remember the state of a match they were playing. This is particularly true when players participate in multiple matches simultaneously.
  • Encode the current state of the match and a set of recent player actions: This is a hybrid strategy that borrows elements from the other two strategies. Essentially, the match data stored on Game Center consists of a recent snapshot of the match plus other data that encodes recent actions since that last snapshot was taken. When the data that records the actions grows too large, your game replays some of those moves to update the match snapshot and then deletes those moves from its list of actions.
  • With this strategy, you typically need to determine which actions the current player has seen (based on when they last took a turn). When your game flattens the match data, it never removes any data that hasn’t been seen by all the participants. This allows all participants to see the moves their opponents made.

And here are some other general guidelines:

  • Encode individual items as compactly as needed. For example, in a 16-player game, you can encode the participant number of the player in 4 bits. Balance the need for compactness with the need for readability in your code. For example, a chess position could be encoded in as little as 6 bits, but in practice a chess match does not approach the match data limit.

Saving Match Data

If the player takes an action that is irrevocable (but control is not yet passed to another player), encode the updated match data and send it to Game Center by calling the SaveCurrentTurn method

byte[] data = System.Text.Encoding.UTF8.GetBytes(CurrentMatch.UTF8StringData + "X");
GameCenter_TBM.Instance.SaveCurrentTurn(CurrentMatch.Id, data);
GameCenter_TBM.ActionMatchDataUpdated += ActionMatchDataUpdated;

void ActionMatchDataUpdated (GK_TBM_MatchDataUpdateResult res) {
	GameCenter_TBM.ActionMatchDataUpdated -= ActionMatchDataUpdated;
	Debug.Log("ActionMatchDataUpdated: " + res.IsSucceeded);
	if(res.IsFailed) {
	} else {

Advancing the State of the Match

Eventually, the current player takes an action that either ends the match or requires another participant to act. Typically, your game encodes the match data, and determines who acts next in the match. Usually, this means encoding the list of all participants in the match in the order they will act next. The reason you do this is that sometimes players drop from a match without forfeiting; they just stop playing. You don’t want the match to end because it is stuck waiting forever for an absent player. Instead, you encode the list of participants so that if one participant forfeits a turn, the match advances to another participant.

Listing bellow shows a typical structure for this code, deferring some of the specific steps to app-specific routines that are based on your game’s actual structure. Those methods represent the places where you need to provide specific implementations in your game. 

byte[] data = System.Text.Encoding.UTF8.GetBytes("Some trun data");

foreach(GK_TBM_Participant p in CurrentMatch.Participants)  {
	//Assuming we have two participants in the current match, so we will trsfar move to the participant who is not equals to current participant
	if(!p.PlayerId.Equals(CurrentMatch.CurrentParticipant.PlayerId)) {
		GameCenter_TBM.Instance.EndTurn(CurrentMatch.Id, data, p.PlayerId);
		GameCenter_TBM.ActionTrunEnded += ActionTrunEnded;

void ActionTrunEnded (GK_TBM_EndTrunResult result) {
	GameCenter_TBM.ActionTrunEnded -= ActionTrunEnded;
	Debug.Log("ActionTrunEnded IsSucceeded: " + result.IsSucceeded);

	if(result.IsFailed) {
	} else {

Setting the Match Outcome When a Participant Leaves a Match

As the match progresses, participants may leave the match. For example, your game logic might determine that a participant has been eliminated from the match.

If the local player resigns from the match and is also the match’s current player, your game must set player outcome and call

void QuitInTurn(string matchId, GK_TurnBasedMatchOutcome outcome, string nextPlayerId, byte[] matchData)

This method is similar to

EndTurn(string matchId, byte[] matchData, string nextPlayerId)

 in that it gives control to another participant. Because of this, you are required to update the match data and provide the next participant. However, your game also provides a match outcome for the player that just exited the match — essentially, a numerical value that indicates why that player left the match. The participant object for that player is updated to include this new match state. Once a participant has exited the match, your game may not make that player the current player.

Game Kit provides some standard values you can use to set the match outcome. 

For example, in Figure 10-4, Bob has just been eliminated from the match. The game chooses to set an outcome of GK_TurnBasedMatchOutcome.Fourth and make Mary the new current player. Bob may still view the match, but may not take actions.

Figure 10-4  Bob has been eliminated from the match

Occasionally a player may resign the game when they are not the current player. To handle this, your game calls the  

void QuitOutOfTurn(string matchId, GK_TurnBasedMatchOutcome outcome)

You provide a match outcome but do not provide new match data or a participant list.

Ending a Match

Eventually, your game logic is going to decide that the match is over. All participants in the match must have a valid match outcome before ending the match. Your game calls the

void EndMatch(string matchId, byte[] matchData)

to formally end the match. This method is similar to the other methods that update the match state, but in this case you do not provide a list of participants because no further actions are allowed in this match. You do update the saved match data to describe how the match ended. Players can still select this match from the matchmaking user interface; your game should display the match ending but not allow the player to take actions.

Example Scenes

You will find basic TBM API using example with the following example scenes:

  • TMB_Multiplayer_Example