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
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Displaying Player Names

At this point you have a functional game, but it’s kinda hard to tell which player you are, especially since it’s random.

So let’s fix this by displaying the player names for each player above their character. Make the following changes to HelloWorldLayer.h:

CCLabelBMFont *player1Label;
CCLabelBMFont *player2Label;

Then switch to HelloWorldLayer.m and add a new method to create the labels right below tryStartGame:

- (void)setupStringsWithOtherPlayerId:(NSString *)playerID {
    
    if (isPlayer1) {

        player1Label = [CCLabelBMFont labelWithString:[GKLocalPlayer localPlayer].alias fntFile:@"Arial.fnt"];
        [self addChild:player1Label];
        
        GKPlayer *player = [[GCHelper sharedInstance].playersDict objectForKey:playerID];
        player2Label = [CCLabelBMFont labelWithString:player.alias fntFile:@"Arial.fnt"];
        [self addChild:player2Label];
        
    } else {
        
        player2Label = [CCLabelBMFont labelWithString:[GKLocalPlayer localPlayer].alias fntFile:@"Arial.fnt"];
        [self addChild:player2Label];
        
        GKPlayer *player = [[GCHelper sharedInstance].playersDict objectForKey:playerID];
        player1Label = [CCLabelBMFont labelWithString:player.alias fntFile:@"Arial.fnt"];
        [self addChild:player1Label];
        
    }
    
}

This creates two CCLabelBMFonts, one for each character. To get the alias for the local player, it can get it by using the GKLocalPlayer singleton. For the other player, it has to look up the GKPlayer object from the GCHelper’s dictionary first.

Next, make some final changes to HelloWorldLayer.m:

// Inside if statement for tryStartGame
[self setupStringsWithOtherPlayerId:otherPlayerID];

// Inside if statement for match:didReceiveData, kMessageTypeGameBegin case
[self setupStringsWithOtherPlayerId:otherPlayerID];

// At beginning of update method
player1Label.position = player1.position;
player2Label.position = player2.position;

Basically when the game is transtioning to the Active state, we know for sure we’ve received the player names and the game is starting, so that’s a good time to set up the labels.

In the update method, each frame it updates the position of the label to the corresponding player’s position.

You might think that’s a bit weird – why not simply add the labels as a child of the sprite instead? Well we can’t do this because the sprites are children of a batch node (hence they can only have other sprites from the same sprite sheet as children). So this is an alternative.

Compile and run your code on two devices, and now you should see the label of each player’s name on top of their character!

Displaying player aliases for each character

Supporting Invites

We have a fully functional game, but let’s put in one more feature for fun and coolness – supporting invites.

You may have noticed when the matchmaker view controller appears, it gives you an option to invite a friend. Right now this wouldn’t work because we haven’t added the code for it yet, but it’s pretty simple so let’s put it in there.

Open up GCHelper.h and make the following changes:

// Add inside GCHelperDelegate
- (void)inviteReceived;

// Add inside @interface
GKInvite *pendingInvite;
NSArray *pendingPlayersToInvite;

// Add after @interface
@property (retain) GKInvite *pendingInvite;
@property (retain) NSArray *pendingPlayersToInvite;

This creates instance variables and properties to keep track of some invite information you’ll see in a bit. It also adds a new method to the GCHelperDelegate to tell it when an invite is received.

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

// At top of file
@synthesize pendingInvite;
@synthesize pendingPlayersToInvite;

// In authenticationChanged callback, right after userAuthenticated = TRUE
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) {
   
   NSLog(@"Received invite");
   self.pendingInvite = acceptedInvite;
   self.pendingPlayersToInvite = playersToInvite;
   [delegate inviteReceived];
   
};

The way invites work in Game Center is that you have to register a callback block to be called when an invite is received. You should register this block as soon as possible after your game starts up – the recommended time is right after a user is authenticated.

For this game, the callback squirrels away the invite info, then notifies the delegate. The delegate will then restart the Cocos2D scene and try to find a match by calling the findMatchWithMinPlayers method – we’ll modify this to take into account any pending invite info.

So modify the findMatchWithMinPlayers method as follows:

- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController delegate:(id<GCHelperDelegate>)theDelegate {

    if (!gameCenterAvailable) return;

    matchStarted = NO;
    self.match = nil;
    self.presentingViewController = viewController;
    delegate = theDelegate;
            
    if (pendingInvite != nil) {
        
        [presentingViewController dismissModalViewControllerAnimated:NO];
        GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:pendingInvite] autorelease];
        mmvc.matchmakerDelegate = self;
        [presentingViewController presentModalViewController:mmvc animated:YES];
        
        self.pendingInvite = nil;
        self.pendingPlayersToInvite = nil;
        
    } else {
        
        [presentingViewController dismissModalViewControllerAnimated:NO];
        GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease]; 
        request.minPlayers = minPlayers;     
        request.maxPlayers = maxPlayers;
        request.playersToInvite = pendingPlayersToInvite;
        
        GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];    
        mmvc.matchmakerDelegate = self;
        
        [presentingViewController presentModalViewController:mmvc animated:YES];

        self.pendingInvite = nil;
        self.pendingPlayersToInvite = nil;
        
    }
    
}

It’s quite similar to last time, except it uses the pendingInvite and pendingPlayersToInvite values if they exist, when creating the GKMatchmakerViewController.

Finally, switch to HelloWorldLayer.m and implement inviteReceived as the following:

- (void)inviteReceived {
    [self restartTapped:nil];    
}

And that’s it! Compile and run your code on two devices, and when one of them starts up, use the GKMatchmakerViewController to send an invite to the other. You should receive a popup like this:

Receiving an invite from Game Center

Tap Accept”, then “Play Now” on the original device, and you should be able to play the game as usual – but this time with a friend of your choice! :]

Where To Go From Here?

Here is a sample project with the completed Cocos2D and Game Center multiplayer networked game we created in this Game Center tutorial series.

If you’re implementing Game Center in your games, you might want to add Leaderboards and Achievements too. If you’re interested in learning about this, check out the chapter on Game Center in Rod’s and my upcoming Cocos2D book!

If there’s something else about Game Center you’re interested in learning about (such as voice chat, or server-hosted games so you can implement MMOs and the like), please feel free to suggest a tutorial! Also, don’t forget to vote for what tutorial you’d like to see next in the sidebar.

If you have any questions, comments, or tips for using Game Center that you’ve learned while making your apps, please join in the forum discussion below!