2011 • 27:31
Game Center is taking multiplayer gaming on iOS one step further with the addition of turn-based game support. Learn about the new API and user interface for turn-based play and see how to manage multiple game sessions. Gain insight into handling turn- based game state and get details about how the turn is passed from player to player.
Speaker: Allan Schaffer
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Hello, I'm Alan Schafer, the Game Technologies Evangelist at Apple. A lot of great games are using Game Center, Apple's social gaming network. And in iOS 5, we're taking multiplayer gaming a step further with new support for churn-based games. So in this video, I'll walk you through the steps you'll take to implement a churn-based game with an emphasis on the UI you'll present to the player and how your game will respond to the user's actions and the actions of the other players. You'll see how to manage multiple matches, how to allow players to take churns, how to handle players dropping out, and how to end the match when the game is over. So let's get started.
Of course, we're all familiar with the mechanics of a turn-based game. There are multiple players and at any given time during the game, it's somebody's turn. That person makes their move or takes their turn and the turn passes to the next player and this is repeated until the game is over.
How it works in Game Center is that at any particular moment, it's somebody's turn, that player makes their move and in doing so, they update the game data for the match they're playing. They send that data to Game Center and tell Game Center to pass the turn to the next player.
The next person then receives a notification that it's their turn. They'll take their phone out of their pocket, run your app, and the app downloads the latest game data. Then that player repeats the process. They take their turn, update the game data, and tell Game Center to pass the turn to the next player. That person gets a notification, downloads the new data, and so on.
Now, a really great feature of Game Center is the ability to auto-match players to fill empty seats. So, when the turn reaches an empty seat, Game Center can auto-match a new player who is waiting to join the game. It's immediately that person's turn. They make their move and play continues.
You, the developer, have total control over how the turn is passed and you'll code this based on the rules of your game. So your code just tells Game Center whose turn it is next and you can pass the turn forward, backwards, you can skip players, you have complete flexibility. It's up to you.
And finally, each player can participate in up to 30 instances of your game simultaneously. Where in some it's their turn, some it's someone else's turn. Up on the top of the screen you see they're starting a new game and waiting to have players auto-matched. And on the right side you see one where the game is over.
So let's recap these capabilities. Each player can participate in up to 30 simultaneous matches in your game. And each match can have up to 16 players. The gameplay is asynchronous and communication with Game Center uses either Wi-Fi or the cellular network. The game data for each match can be up to 64K and I'll discuss various strategies for handling this later in the talk. And you, the developer, determine how the turns are passed. And finally, players join the games either by accepting invites or by auto-matching.
So those are the capabilities from the Game Center perspective. But now let's think about how a turn-based game needs to be architected because it's different than a single player arcade style game or a synchronous multiplayer game. The first thing to understand is that your app needs to be able to manage multiple matches being played simultaneously by each player up to 30. And each match is in a different state with different game data, different opponents, and ultimately each will have a unique outcome.
The second difference is that in each match, it's only one player's turn at a time. The other players can observe, but they can't play until it's their turn. Think about a game of chess. While it's your turn, I can look at the board and plan my next move, but I can't move any pieces until it becomes my turn. So your app has to be coded to enforce rules like that one.
The third thing to understand is that turn-based games are asynchronous. So your app is not always running for the entire duration of the game. Instead, the expectation is that the player will run your app, choose one of the matches that they're playing, and then your app will download the latest game data from Game Center so it can know what's happened. And last is to realize that because turn-based games are asynchronous and because you can have multiple turn-based games, you can't run them. So you have to be careful.
If you have multiple matches running simultaneously, that your players could be anywhere in the app when various events occur. Maybe they're looking at a match or making a move, or maybe they're tapping around on the main menu when someone else makes their move and it becomes that player's turn. You need to handle this thoughtfully. You shouldn't just yank them out of what they were doing and switch to that other match. You need to think about putting up a notification, saying what's happening, or just let them act accordingly. accordingly.
Okay, so with that conceptual introduction, now let's get started and look at the API for implementing a turn-based game. So there's four main classes you'll use for turn-based games. The first is the GK turn-based match. This is the main entry point for the API and essentially the central nervous system for each match. Each match is an instance of the game with a list of participants, a pointer to the game state, and the ID of the player whose turn it is right now.
The way the match works is that the player selects a match they want to play. You use the match API to retrieve the latest game data from Game Center. The player takes their turn and this updates the game state. And then your app passes the turn to the next player. This continues until we break out of this loop when the match ends, meaning the game is over.
The second class is the GK turn-based participant. This is either a person or an empty slot and there can be up to 16 participants in a match. Each participant contains a player ID, a status, and an outcome. There's an interesting detail about the player ID. It has the player's Game Center ID or it can be nil if we're waiting for someone to be auto-matched.
The status will tell you the state of the player: invited or matching before we start, active while the match is running, and done after the match ends or if that person drops out. The outcome is something you fill in when the game ends to tell the player how they did.
The third class is the GK turn-based event handler. The delegate object you set up for this class receives all of the external events for your game. For example, when the player receives an invite or the invite sequence has been initiated from the Game Center app. You'll also receive events each time someone takes their turn and when the match has ended.
The fourth class is the GK Turn-Based Matchmaker View Controller. This is the hub of all the user activity in your game. Its main purpose is to manage the matches the player is participating in. It lets them pick a match to play or to quit or to remove the match from the list. And it also allows the users to start a new match either by inviting friends or by auto matching.
But now let me take you on a tour through the UI of the view controller. And then we'll use this to branch out into different topics that you'll need to handle in your code. First, starting with this button at the top for invites and matchmaking. This is how the user starts a new match by inviting their friends or asking to be auto-matched. And once they're in the match, it'll immediately become their turn and that match will show up on the list down below.
This next area here shows the matches where it's this player's turn right now. We see the profile photo of their opponent and when that person took their turn. And if we tap one of these matches, it's expected that you'll dismiss the view controller, take the player into the game, and allow them to play their turn. Scrolling down.
This next part shows the matches where it's someone else's turn right now. So we're waiting for Jessica and Eunice to take their turns and in this third one we're waiting for a new player to be auto-matched against us and take their turn. And if we tap one of these matches it's expected that you'll take the player into the game and download the latest game state from Game Center but it's not our turn so you wouldn't allow the player to make any moves. They can only observe or maybe they can quit.
Speaking of quitting a match, the player can swipe one of the matches to bring up a button to quit. In this case, they're quitting out of turn. I'll show you how we handle that a bit later. And finally, this section at the bottom showing matches that have ended and their outcome.
I can swipe these to remove them from the list, or if I tap one, again, I expect the app to take me into the game, download the final game data from Game Center, and show me the final state of this match. For example, the checkmate move. So, now let's look at the implementation.
And we'll use the view controller as the jumping off point. So we need code that will set up the view controller and then code that handles all the possible actions the user might take. There's four of those possible actions. They can go to the invite screen and start a new match or ask to be auto-matched.
They can select one of the matches and it might be one where it's their turn, someone else's turn, or one where the match has ended. They can remove a match from the list, one where it's their turn, someone else's turn, or one where the match has ended. And last, they can cancel out to dismiss the view controller.
So let's start with setting up the view controller. First, I actually need to create a match request in case the user taps the plus button to create a new match. So we create a GK match request, set the minimum and maximum number of players and any other attributes for the match. Next, we create and initialize the view controller and pass in the match request that we just created.
Next, we set some options. For example, it's possible to create your own UI to show the list of matches. But here, we're just saying that we want to use the built-in UI. And we set a delegate. And finally, we present the view controller and we're off and running.
Now the view controller is up and if the user taps any of the matches the delegate method "turn-based matchmaker view controller did find match" will be called. And as I said earlier, what's expected is that you'll dismiss the view controller and take the player into the game. Then you'll download the game data for the match that they selected, unpack it and show the state of the match to the player. If it were a chess game for example you'd show them the board. And what to do from there depends on whether it's this player's turn now or someone else's. If it is their turn you'll let them take their action. So let's look at how that's implemented.
Here's that delegate method turn-based matchmaker view controller did find match. We're being passed the view controller and the match they selected, so we dismiss the view controller and go into the game. And start by downloading the game data for this match. And then we have a block where once it's downloaded, we'll unpack it into our own internal representation.
We use that to display the game to the user, and then if it's this player's turn, we'll let them take their action in the match. And take note of how we find out whether it's this player's turn. One of the properties of the match is the current participant. That's the participant whose turn it is right now. So we compare the player ID of the current participant with our player ID. If they're equal, then it's our turn. Okay, now let me go back for a moment and talk more about the match data.
The match data contains all of the game state for a specific match. This has to contain all of the data that you need to fully represent the current state of the game to each player. It's an NSData object. You define its contents and you pack the game state into it before you upload it to Game Center and unpack it when you download it. Game Center stores this data for all of your matches and it's visible by all of the participants in those matches. But note that only the current participant can modify it, the player whose turn it is right now.
and it has a limited size 64K. So you have to be very wise about how the data is stored. If you have a lot of different elements, you will certainly want to tightly pack them together yourself, probably into a C struct instead of, for example, using an NSDictionary and assuming that it will be packed up efficiently.
And if 64K isn't enough space, then another alternative would be to use the match data to index into data that you maintain on your own server. So we've shown the view controller, let the user create matches and select a match. We talked about downloading the game data and we check to see whether it's our turn or not. So next, let me show you how you can take your turn.
Now every game has its own logic and implementation for letting the player take their turn. If it were chess, for example, the player moves a piece. In a card game, they play their hand, all according to the rules of the game. The point at which Game Center applies to this is when it becomes time to pass the turn to the next player. And there's a subtle distinction here between taking your turn and passing the turn.
In chess, for example, after you've moved a piece, it implicitly becomes your opponent's turn. But other games might let you take multiple actions before passing the turn. In a trivia game, for example, perhaps you get to keep answering questions and playing your turn until you answer incorrectly, and then the turn passes to your opponent.
So when it is time to pass the turn, what you'll do is to update the match data with the latest game state. Your code will choose which player goes next. There's an optional message that can be displayed to the other players. And then you'll call the Game Center API to pass the turn. So let's take a look at that.
So here it is. It was our turn and we've taken our action. So we update the state of the match based on the action we took and the rules of the game. Then we choose who goes next. And I'll come back to this in a moment. We can set an optional message that will be shown to the other participants.
And then here is where the work is done. We tell the match to end our turn and we pass in who goes next, the updated match data, and there's a completion handler. For when this is finished to check for errors. So this uploads the new match data to Game Center and triggers a notification to the next participant that it's now their turn.
But let's go back. How do we choose who goes next? Well, it's up to you, the developer, to code this logic into your app based on the rules of the game. Some games only have two players, so the turn just passes back and forth. Other games with more players have different logic for passing the turn. But the bottom line is, each match has an array of participants, and your app tells Game Center which one's next. But there's one thing you have to be aware of, and it's that a player might quit the match before the game is over.
And it's important that you do not pass the turn to a player who has quit. So let's look at an example of how you might implement this. The match has an array of participants, and I'm starting out just by getting the index of the player whose turn it is right now. My plan is to pass the turn to whoever is next in the array, and go back to index zero if I'm at the last participant. So that's what I'm doing here. I get the object at I plus one, modulo the count of the participants.
However, I need to make sure that this next player has not quit, so I check their status. If it's not done, it means they're still in the game, and they go next. But if it is done, then I increment the index, and go to the next person, and repeat the process.
And I keep iterating until I find a player who is still in the game. Or in this case, if I come back around to myself, it means that everyone else has quit, and I need to handle that. Now, it was my turn, I've taken my action, and I've told Game Center to pass the turn.
So what happens on the other side? What do the other players see? Well, the information about me taking my turn will come to them through their GK Turn-Based Event Handler. If they have the app running, the delegate method "handleTurnEventForMatch" will be called on their side. And note, it'll actually be called each time anybody makes a turn in any match that they're participating in. So anyways, somebody took a turn in some match. It's not necessarily that person's turn now.
What should they do? Well, remember, that player could be anywhere in the app when all of this happens. So you probably just want to inform them that I took my turn. If they happen to be looking at the match that I'm in, then update its UI. And if it's now become their turn, then tell them. So let's take a look at all of this.
So someone in some match has taken their turn, so handleTurnEventForMatch is called and we're past the match object. What you do next is up to you, but in this example, I'm looking to see if it's now my turn. If it is, I use the new GK notification banner feature to display a banner to the user saying it's your turn along with an optional match message.
And if the user happened to be looking at the match that someone just took a turn in, I should update its UI. But notice that I'm not doing anything here to immediately take the player into making their turn or pull them into a particular match. Because this event could happen any time and you want to allow the user to just keep doing what they were doing before. They can navigate back to the view controller themselves to take their turn when they're ready.
So let's recap taking turns. Some things to remember. First, remember that only the current player, meaning the person whose turn it is right now, only that player has the ability to update the match data and they update it when they pass the turn. Everyone else can only retrieve the current match data.
Likewise, only the person whose turn it is right now has the ability to pass the turn to the next player. Third, when you're passing the turn, be aware that players might quit or might be eliminated and so on, and you need to make sure you don't pass the turn to someone who has already quit. And finally, understand that everybody in the match will receive turn events every time anyone takes a turn, and you should just include some unobtrusive UI to inform the player what's happening. happened.
Next, I mentioned that players can quit the match anytime. So let's talk more about what you do to handle that. The scenario is that the player is actively in a match but decides to resign before the game is over. They can do this from our view controller or you might have some other UI in the game that allows them to quit.
How you handle this depends on whether they quit while it's their turn or if they quit while it's someone else's turn. If they quit while it's their turn, your code needs to update the game state and set that player's outcome and most importantly, it needs to tell Game Center who should go next. Now, if it's not their turn, it's much easier. You just tell Game Center that they've quit and we handle the rest. So, let's see how this is done.
Let's say it's this player's turn right now and they've swiped a match in the view controller and quit. The view controller delegate method turn-based matchmaker view controller player quit for match will be called, passing the view controller and the match they've selected. Next, you'll update the game data if it's appropriate to do so. You'll choose who goes next, just as you normally would. And then you tell the match, this participant quit in turn with outcome quit, passing the next participant, the updated match data, and a block that's called after this is sent.
And that's it. Now, this player's quit and the turn gets passed to the next person. And one quick side note, this code is how you handle the player quitting in turn from our view controller. But if you had your own UI to quit, you would just call this same method, participant quit in turn with outcome.
Likewise, if the player quits while it's someone else's turn, the only thing we need to do is inform Game Center. Now, if the player does it from the view controller, we handle it internally. But if you have your own UI for managing matches and want to allow this, then you just tell the match participant quit out of turn with outcome quit and give a completion handler block for when it's finished.
All right, we've discussed the view controller, selecting a match, taking turns, receiving turn events, and resigning. And the next topic is how we end the match when the game is over. So this is about when a match ends naturally. The game is over because of the rules of the game. In chess, for example, it's checkmate.
So when a match ends, we need to set an outcome for all the participants. And I've listed them here. That the player won, lost, tied, or came in first place, second, third, fourth, and so on. Or if these outcomes aren't the ones that you need, then there's 255 custom outcomes that you can use. You'll also set an optional message and then inform Game Center that the match has ended. A notification will then be sent to all the other participants and the game will be over. Note, only the player whose turn it is right now can end the match.
So here's an example. I start out by setting the outcome for each participant. In this case, everybody's tied. I update the match data with the final state of the game and then set a message that will be shown to all the other players. And then finally, here's where I inform Game Center. I tell the match and match in turn with match data and pass the final game state and a completion handler block. And that's it. The game is now over.
On the other side, what the other players receive is a notification that gets handled by their event handler. So on their side, the delegate method handle match ended gets called and they're past the match that's just ended. And just like receiving a turn event, what you'll do is very game dependent. In this case, I'm just displaying a banner to inform the user the match has ended. And if the user happened to be looking at the match when it ended, I'll download the final match data and update the UI to show the outcome.
That's almost everything. Just two loose ends I want to mention before we're finished. The first is about handling being launched from the Game Center app itself. So within the Game Center app, the user can go look at one of their friends, look at the games they have in common with that friend, and if it's a turn-based game, they can start the invite sequence and then press play. This launches your app to actually send the invite.
Now that's handled by the GK turn-based event handler. There's a delegate method handle invite from Game Center that gets called and it's passed an array of players. And what you're expected to do is to create a match request with the players to invite property set to this array that was passed to you. And then present the view controller so the user can send the invite. Now this is something many applications overlook, but it's important to implement and handle correctly.
The other loose end is about customizing the view controller. You're able to replace our default match list with your own UI. And the GK turn-based match has a class method, load matches with completion handler. And that will give you an array containing all the matches. And you can step through these and display their data in your own custom interface. Now, if you do that, then you need to decide how you're going to handle invites and matchmaking.
Because invites can only be sent from our built-in UI. So if you want to support invites, you'll use the built-in UI, but tell it not to show the match list. You'll handle that yourself. If you don't want to support invites and just want to do auto-matching, you can do that programmatically using find match for request with completion handler.
All right, so I've covered a lot, all wrapped up in those four classes. The view controller handles invites and matchmaking and lets a user select a match to play or quit from a match. The turn-based match is the main class managing the state of the match, letting players take turns and end the game.
Each player is represented by a turn-based participant, which you'll watch for status changes. And external events arrive through the turn-based event handler. So if you have questions about Game Center or the content I've presented here, please contact me at ashaffe at apple.com. We also have some great documentation available in the iOS Dev Center, and you can find me in the developer forums as well. Thanks for watching.