Game Center Tutorial for iOS: How To Make A Simple Multiplayer Game: Part 2/2

The second part of a Game Center tutorial series that shows you how to create a simple multiplayer iPhone game. By Ray Wenderlich.

Leave a rating/review
Save for later
Share

Create a simple multiplayer game with Game Center and Cocos2D!

Create a simple multiplayer game with Game Center and Cocos2D!

Create a simple multiplayer game with Game Center and Cocos2D!

This is the second part of a two part Game Center tutorial series on creating a simple networked game with Game Center matchmaking.

In the first part of the Game Center tutorial series, you learned how to enable Game Center for your app and find matches with other players using the built-in GKMatchmakerViewController.

In this second and final part of the Game Center tutorial series, you’ll learn how to look up player aliases, how to send and receive data between games, and how to support invites.

In the end, you’ll have a fully functional (but simple) networked game that uses Cocos2D and Game Center that you can play with your friends!

If you haven’t got it already, start by downloading the sample project where we left it off in the last Game Center tutorial.

Network Code Strategy: Picking Players

Before we pick things back up, let’s talk about the strategy we’re going to take with our network code, starting with how to pick players.

We have two players in this game: player 1 (the dog) and player 2 (the kid). The problem is, how do we decide who should be the dog and which the kid?

The strategy we’re going to take is each side will generate a random number on startup, and send it to the other side. Whichever side has the larger random number will be player 1, and the other side will be player 2.

In the rare event each side generates the same random number, we’ll just try again.

Whichever player is player 1 will get some special “privileges.” First, that side signals the game should start by sending a message to the other side. That side also is the one who is responsible for checking when the game ends, and sending a message to the other side when the game is over (and who won).

In other words, “player 1” takes the role of the “server”. He’s the final authority on what goes on for this game.

Network Code Stragegy: Player Aliases

Since it’s random which player is the dog and which is the kid, we need to have some way of showing the player which one he is.

For this game, we’ll just print the player’s alias right on top of the character they are, so they can tell who they are.

If you don’t know what a player alias is, it’s the nickname that you set up for your account in Game Center. When a match is made, you don’t get these automatically – you have to call a method so Game Center will send them to you.

Network Code Strategy: Game States

One of the challenges with making networked code is that things can happen in different orders than you might expect.

For example, one side may finish initializing the match and send the random number to the other side before the other side even finishes initializing the match!

So if we’re not careful, we can get into strange timing issues in our code that causes problems. One good way to keep things clean is to keep careful track of what state each side of the game is currently in.

The following diagram illustrates the states Cat Race will need:

Game states for this simple networked game

Let’s go through each of these:

  • Waiting for Match: The game is waiting for a match to be connected, and for the player aliases to be looked up. When both of these are complete, it checks to see if it’s received the random number from the other side yet, and advances to the next state appropriately.
  • Waiting for Random #: The game has a match and has the player aliases, but is still waiting to receive a random number from the other side.
  • Waiting for Start: The game is waiting for the other side to start the game (this occurs when the game is player 2).
  • Active: The game is currently playing – no winner has been found yet. Every time a player moves, they will tell the other side that they’ve moved by sending a message.
  • Done: The game is over (player 1 has sent a message to player 2 to tell him that the game is over). No more messages are sent and the playre can then restart the game.

OK now that you have a plan in mind, let’s move onto the first step, which is retrieving the player aliases!

Looking up Player Aliases

Switch to GCHelper.h and make the following changes:

// Add inside @interface
NSMutableDictionary *playersDict;

// Add after @interface
@property (retain) NSMutableDictionary *playersDict;

This defines an instance variable and a property for a dictionary that will allow easy lookup of GKPlayer data (which includes the player’s alias) based on a player’s unique ID.

Then switch to GCHelper.m and make the following changes:

// Add to top of file
@synthesize playersDict;

// Add new method after authenticationChanged
- (void)lookupPlayers {
    
    NSLog(@"Looking up %d players...", match.playerIDs.count);
    [GKPlayer loadPlayersForIdentifiers:match.playerIDs withCompletionHandler:^(NSArray *players, NSError *error) {
       
        if (error != nil) {
            NSLog(@"Error retrieving player info: %@", error.localizedDescription);
            matchStarted = NO;
            [delegate matchEnded];
        } else {
            
            // Populate players dict
            self.playersDict = [NSMutableDictionary dictionaryWithCapacity:players.count];
            for (GKPlayer *player in players) {
                NSLog(@"Found player: %@", player.alias);
                [playersDict setObject:player forKey:player.playerID];
            }
            
            // Notify delegate match can begin
            matchStarted = YES;
            [delegate matchStarted];
            
        }
    }];
    
}

// Add inside matchmakerViewController:didFindMatch, right after @"Ready to start match!":
[self lookupPlayers];

// Add inside match:playerdidChangeState, right after @"Ready to start match!":
[self lookupPlayers];

The main logic occurs inside lookupPlayers. This is called when the match is ready, and it looks up the info for all of the players currently in the match (except for the local player – we already have that info via the GKLocalPlayer singleton!)

Game Center will return a GKPlayer object for each player in the match as a result. To make things easier to access later, this code puts each GKPlayer object in a dictionary, keyed by player ID.

Finally, it marks the match as started and calls the game’s delegate so it can start the game.

Before we move onto that though, test it out! Compile and run your code on two devices, and this time when you examine your console output you should see the players looked up and the game ready to go:

CatRace[16918:207] Authentication changed: player authenticated.
CatRace[16918:207] Player connected!
CatRace[16918:207] Ready to start match!
CatRace[16918:207] Looking up 1 players...
CatRace[16918:207] Found player: Vickipsq
CatRace[16918:207] Match started