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

Ray Wenderlich
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

Adding Network Code

You have the match set up and the player names available, so now you’re on to the real meat of the project – adding the network code!

The first thing you need to do is define a few new game states based on our diagram from earlier. Open up HelloWorldLayer.h and modify your GameState structure to be the following:

typedef enum {
    kGameStateWaitingForMatch = 0,
    kGameStateWaitingForRandomNumber,
    kGameStateWaitingForStart,
    kGameStateActive,
    kGameStateDone
} GameState;

You also need to add a new reason the game can end – disconnection. So modify the EndReason enum as follows:

typedef enum {
    kEndReasonWin,
    kEndReasonLose,
    kEndReasonDisconnect
} EndReason;

Next you need to define some structures for the messages you’ll be sending back and forth. So add the following to the top of HelloWorldLayer.h:

typedef enum {
    kMessageTypeRandomNumber = 0,
    kMessageTypeGameBegin,
    kMessageTypeMove,
    kMessageTypeGameOver
} MessageType;
 
typedef struct {
    MessageType messageType;
} Message;
 
typedef struct {
    Message message;
    uint32_t randomNumber;
} MessageRandomNumber;
 
typedef struct {
    Message message;
} MessageGameBegin;
 
typedef struct {
    Message message;
} MessageMove;
 
typedef struct {
    Message message;
    BOOL player1Won;
} MessageGameOver;

Note that each message starts with a message type – this is so that you have a known number you can look at for each message to identify what type of message it is.

Finally add a few more instance variables to the HelloWorldLayer class:

uint32_t ourRandom;   
BOOL receivedRandom;    
NSString *otherPlayerID;

These will keep track of the random number for this device, whether we’re received the random number from the other side, and the other player’s player ID.

OK, now let’s start implementing the networking code. Switch to HelloWorldLayer.m and modify the matchStarted method as follows:

- (void)matchStarted {    
    CCLOG(@"Match started");        
    if (receivedRandom) {
        [self setGameState:kGameStateWaitingForStart];
    } else {
        [self setGameState:kGameStateWaitingForRandomNumber];
    }
    [self sendRandomNumber];
    [self tryStartGame];
}

So when the match starts up, we check to see whether we’ve already received the random number (which can happen due to timing), and set the state appropriately. Then it calls methods (which you’re about to write) to send the random number and maybe start the game.

Let’s start with sendRandomNumber. Make the following changes to HelloWorldLayer.m:

// Add at bottom of init, and comment out previous call to setGameState
ourRandom = arc4random();
[self setGameState:kGameStateWaitingForMatch];
 
// Add these new methods to the top of the file
- (void)sendData:(NSData *)data {
    NSError *error;
    BOOL success = [[GCHelper sharedInstance].match sendDataToAllPlayers:data withDataMode:GKMatchSendDataReliable error:&error];
    if (!success) {
        CCLOG(@"Error sending init packet");
        [self matchEnded];
    }
}
 
- (void)sendRandomNumber {
 
    MessageRandomNumber message;
    message.message.messageType = kMessageTypeRandomNumber;
    message.randomNumber = ourRandom;
    NSData *data = [NSData dataWithBytes:&message length:sizeof(MessageRandomNumber)];    
    [self sendData:data];
}

sendRandomNumber creates a new MessageRandomNumber structure, and sets the random number to a random number that was generated in init. It then converts the structure into an NSData to send to the other side.

sendData calls the sendDataToAllPlayers method in the GCHelper’s match object to send the packet to the other side.

Next implement the tryStartGame method you referred to earlier. Make the following changes to HelloWorldLayer.m:

// Add right after sendRandomNumber
- (void)sendGameBegin {
 
    MessageGameBegin message;
    message.message.messageType = kMessageTypeGameBegin;
    NSData *data = [NSData dataWithBytes:&message length:sizeof(MessageGameBegin)];    
    [self sendData:data];
 
}
 
// Add right after update method
- (void)tryStartGame {
 
    if (isPlayer1 && gameState == kGameStateWaitingForStart) {
        [self setGameState:kGameStateActive];
        [self sendGameBegin];
    }
 
}

This is quite simiple – if it’s player 1 (who has the special privilege of acting like the “server”) and the game is ready to go, it sets the game to active, and tells the other side to do the same by sending a MessageGameBegin to the other side.

OK – now for the code that handles receiving messages from the other side. Modify your match:didReceiveData:fromPlayer method to be the following:

- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
 
    // Store away other player ID for later
    if (otherPlayerID == nil) {
        otherPlayerID = [playerID retain];
    }
 
    Message *message = (Message *) [data bytes];
    if (message->messageType == kMessageTypeRandomNumber) {
 
        MessageRandomNumber * messageInit = (MessageRandomNumber *) [data bytes];
        CCLOG(@"Received random number: %ud, ours %ud", messageInit->randomNumber, ourRandom);
        bool tie = false;
 
        if (messageInit->randomNumber == ourRandom) {
            CCLOG(@"TIE!");
            tie = true;
            ourRandom = arc4random();
            [self sendRandomNumber];
        } else if (ourRandom > messageInit->randomNumber) {            
            CCLOG(@"We are player 1");
            isPlayer1 = YES;            
        } else {
            CCLOG(@"We are player 2");
            isPlayer1 = NO;
        }
 
        if (!tie) {
            receivedRandom = YES;    
            if (gameState == kGameStateWaitingForRandomNumber) {
                [self setGameState:kGameStateWaitingForStart];
            }
            [self tryStartGame];        
        }
 
    } else if (message->messageType == kMessageTypeGameBegin) {        
 
        [self setGameState:kGameStateActive];
 
    } else if (message->messageType == kMessageTypeMove) {     
 
        CCLOG(@"Received move");
 
        if (isPlayer1) {
            [player2 moveForward];
        } else {
            [player1 moveForward];
        }        
    } else if (message->messageType == kMessageTypeGameOver) {        
 
        MessageGameOver * messageGameOver = (MessageGameOver *) [data bytes];
        CCLOG(@"Received game over with player 1 won: %d", messageGameOver->player1Won);
 
        if (messageGameOver->player1Won) {
            [self endScene:kEndReasonLose];    
        } else {
            [self endScene:kEndReasonWin];    
        }
 
    }    
}

This method casts the incoming data as a Message structure (which always works, because we’ve set up each structure to begin with a Message structure). It can then look at the message type to see which type of message it actually is.

  • For the MessageRandomNumber case, it checks to see if it’s player 1 or player 2 based on the random number, and advances to the next state if it was waiting for the random number (and also possibly starts the game up if it’s player 1).
  • For the MessageGameBegin case, it just switches the game to active, since this means that player 1 has just sent a begin message to player 2.
  • For the MessageMove case, it moves the other player forward a bit.
  • For the MessageGameOver case, it ends the scene based on the appropriate reason.

You’re almost done! You have most of the game logic in place, you just have a few finishing tidbits to add here and there. Make the following changes to HelloWorldLayer.m next:

// Modify setGameState as follows
// Adds debug labels for extra states
- (void)setGameState:(GameState)state {
 
    gameState = state;
    if (gameState == kGameStateWaitingForMatch) {
        [debugLabel setString:@"Waiting for match"];
    } else if (gameState == kGameStateWaitingForRandomNumber) {
        [debugLabel setString:@"Waiting for rand #"];
    } else if (gameState == kGameStateWaitingForStart) {
        [debugLabel setString:@"Waiting for start"];
    } else if (gameState == kGameStateActive) {
        [debugLabel setString:@"Active"];
    } else if (gameState == kGameStateDone) {
        [debugLabel setString:@"Done"];
    } 
 
}
 
// Add new methods after sendGameBegin
// Adds methods to send move and game over messages
- (void)sendMove {
 
    MessageMove message;
    message.message.messageType = kMessageTypeMove;
    NSData *data = [NSData dataWithBytes:&message length:sizeof(MessageMove)];    
    [self sendData:data];
 
}
 
- (void)sendGameOver:(BOOL)player1Won {
 
    MessageGameOver message;
    message.message.messageType = kMessageTypeGameOver;
    message.player1Won = player1Won;
    NSData *data = [NSData dataWithBytes:&message length:sizeof(MessageGameOver)];    
    [self sendData:data];
 
}
 
// Add to beginning of ccTouchesBegan:withEvent
// Sends move message to other side when user taps, but only if game is active
if (gameState != kGameStateActive) return;
[self sendMove];
 
// Add to end of endScene:
// If the game ends and it's player 1, sends a message to the other side
if (isPlayer1) {
    if (endReason == kEndReasonWin) {
        [self sendGameOver:true];
    } else if (endReason == kEndReasonLose) {
        [self sendGameOver:false];
    }
}
 
// Add to beginning of update:
// Makes it so only player 1 checks for game over conditions
if (!isPlayer1) return;
 
// Add at bottom of matchEnded
// Disconnects match and ends level
[[GCHelper sharedInstance].match disconnect];
[GCHelper sharedInstance].match = nil;
[self endScene:kEndReasonDisconnect];
 
// Add inside dealloc
// Releases variable initialized earlier
[otherPlayerID release];
otherPlayerID = nil;

The above code is pretty simple (and commented to tell you what it does) so we won’t dwell on it further here.

Phew! That was a lot of code, but the good news is it’s DONE! Compile and run your code on two devices, and you should be able to have a complete race!

A multiplayer game using Game Center and Cocos2D

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!

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

User Comments

100 Comments

[ 1 , 2 , 3 , 4 , 5 , 6 , 7 ]
  • just tell me one thing please how can i add multiplayer functionality ?? just multiplayer functionality code i want
    aliqamar
  • Hi,

    Thanks for the great tutorial. I have implemented it in my game. Everything works fine except inviting a friend. In the multiplayer start screen, when I clicked on the invite friends button and selected one of my friends, it says waiting... forever and my friend's iPhone doesn't receive an invitation/notification from Game center.

    I also downloaded the final code of the tutorial and it also doesn't work.

    Some early feedback about possible answers:


    • I use two devices (one iPhone and one iPad), no simulator

    • All settings on iTunes connect are fine including multiplayer settings

    • I validated that both devices are connected to sandbox by using different test accounts

    • I've already checked the notification settings for Game center on both devices

    • I've already checked all proxy/firewall issues and tried on both WiFi and Cellular for both devices

    • Game invitations are enabled for both of the devices/accounts

    • I've already checked the bundle IDs, app version IDs, etc...

    • Both of the devices are IOS 6.x and App target version os IOS 5.0

    • I have no other issues about game center (leaderboards, random matchmaking, etc... all fine)

    • I call the inviteHandler method as soon after I authenticated a user as possible as mentioned in Apple documentation.


    I have also posted the same question to stackoverflow with more detailed explanation. Here is the link http://stackoverflow.com/questions/1624 ... nvitations

    Any help will be appreciated.
    oktemre
  • Anyone to help?

    oktemre wrote:Hi,

    Thanks for the great tutorial. I have implemented it in my game. Everything works fine except inviting a friend. In the multiplayer start screen, when I clicked on the invite friends button and selected one of my friends, it says waiting... forever and my friend's iPhone doesn't receive an invitation/notification from Game center.

    I also downloaded the final code of the tutorial and it also doesn't work.

    Some early feedback about possible answers:


    • I use two devices (one iPhone and one iPad), no simulator

    • All settings on iTunes connect are fine including multiplayer settings

    • I validated that both devices are connected to sandbox by using different test accounts

    • I've already checked the notification settings for Game center on both devices

    • I've already checked all proxy/firewall issues and tried on both WiFi and Cellular for both devices

    • Game invitations are enabled for both of the devices/accounts

    • I've already checked the bundle IDs, app version IDs, etc...

    • Both of the devices are IOS 6.x and App target version os IOS 5.0

    • I have no other issues about game center (leaderboards, random matchmaking, etc... all fine)

    • I call the inviteHandler method as soon after I authenticated a user as possible as mentioned in Apple documentation.


    I have also posted the same question to stackoverflow with more detailed explanation. Here is the link http://stackoverflow.com/questions/1624 ... nvitations

    Any help will be appreciated.
    oktemre
  • also having issues with inviting friends (it just goes to 'failed' almost immediately) the 2 are able to connect via auto match
    sir_seagill
  • well.... after invites wouldn't work between my ipod and simulator yesterday, I tested it with a friend device to device and it worked. :D so it seems like the simulator doesn't support it at all or the simulator functionality is temporary down.
    sir_seagill
  • Typo: Network Code Stragegy: Player Aliases
    brrreak
  • Hi Ray,

    Thank you for great tutorial.

    But its not working when I connect the iOS 6 to iOS 6. From iOS 5 to 6 is ok.

    Can you help me with this?

    Thanks,
    mariammkrtchyan
  • Doh wrong thread ;)
    sqwert
  • I have been experimenting with this game after finishing the tutorial and have hit a couple snags.

    I wanted to add a 'chat room' type feature where the users could talk to each other. I have had no problem getting the input from the user, but it seems that you can not send objects in structs. Because NSString's are objects, and because this tutorial relies heavily on the structs I can not find a way to implement this.

    When I do attempt to send an NSString (or any object) to the other player, their devices recognizes the message, however it returns a 'bad access' error when trying to read the object from the struct. Any help trying to get this working and sorted out would be gladly appreciated! I have been pulling my hair out trying to figure this out.

    Also, if it is not too much more to ask- I want to be able to send an NSArray and a UIImage between users. The NSArray contains nothing but NSStrings.

    Thanks in advance for help from anyone!
    beeedy
  • I was able to figure it out after reading through the documentation a while. For anyone else interested, because I feel like there are a few (as being able to send objects is very useful) I will do a quick run down.

    First off you need to scrap all the structs that you built in the tutorial. Structs are only able to hold primitive data types (BOOL, int, char, etc.). Then create a custom class that is a subclass of NSObject, giving it a name that pleases you. In this class create a property for every struct we had and any info you may wish to send back and forth. In the .m file implement encodeWithCoder:(NSCoder*)coder and implement initWithCoder:(NSCoder*)decoder. Look up what these two functions should contain. Now just create an instance of your object, fill with values of your pleasing, use NSKeyedArchive to convert to NSData, and off you can send it!
    beeedy
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Non-Gaming!

    Loading ... Loading ...

Last week's winner: How to Make a Simple 2D Game with Metal.

Suggest a Tutorial - Past Results

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in October: Xcode 6 Tips and Tricks!

Sign Up - October

Our Books

Our Team

Tutorial Team

  • Bill Kastanakis

... 53 total!

Update Team

... 14 total!

Editorial Team

... 22 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

  • Richard Casey

... 4 total!