Game Center Tutorial: How To Make A Simple Multiplayer Game with Sprite Kit: Part 2/2

Learn how to make a simple multiplayer racing game with Sprite Kit in this Game Center tutorial! By Ali Hafizji.

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.

Ending the Match

At this point you can play the game on two devices however you have not added any end condition. Your game needs to be in a position to end the game and determine a winner. To do this make the following changes to the MultiplayerNetworking class.

//Add to MultiplayerNetworking.m
- (void)sendGameEnd:(BOOL)player1Won {
    MessageGameOver message;
    message.message.messageType = kMessageTypeGameOver;
    message.player1Won = player1Won;
    NSData *data = [NSData dataWithBytes:&message length:sizeof(MessageGameOver)];
    [self sendData:data];
}

//Declare the above method in MultiplayerNetworking.h
- (void)sendGameEnd:(BOOL)player1Won;

Switch to GameScene.m and make the following changes to the update: method:

-(void)update:(CFTimeInterval)currentTime {
    if (self.paused && _currentPlayerIndex == -1) {
        return;
    }
    //Only Player 1 will check for game over condition
    if (_currentPlayerIndex == 0) { // Here!
      [_players enumerateObjectsUsingBlock:^(PlayerSprite *playerSprite, NSUInteger idx, BOOL *stop) {
          if(playerSprite.position.x + playerSprite.size.width/2 > _cat.position.x) {
              BOOL didWin = NO;
              if (idx == _currentPlayerIndex) {
                  NSLog(@"Won");
                  didWin = YES;
              } else {
                  //you lost
                  NSLog(@"Lost");
              }
              self.paused = YES;
              _currentPlayerIndex = -1;
              *stop = YES;
            
              [_networkingEngine sendGameEnd:didWin]; // Here!
              if (self.gameOverBlock) {
                  self.gameOverBlock(didWin);
              }
          }
      }];
    } // Here!
}

The way the above code works is that the game first checks if the local player is player1. If yes it enumerates through each player and checks to see if any player has crossed the cat. If a player has crossed the cat player 1 sends out the game over message.

Next you need add code to receive the game over message and show the user the game over view. To do this make the following changes:

// Add to MultiplayerNetworkingProtocol in MultiplayerNetworking.h
- (void)gameOver:(BOOL)player1Won;

// Add to the end of the section that handles the game over message in match:didReceiveData:fromPlayer: in MultiplayerNetworking.m
MessageGameOver * messageGameOver = (MessageGameOver *) [data bytes];
[self.delegate gameOver:messageGameOver->player1Won];

// Add the new method to GameScene.m
- (void)gameOver:(BOOL)player1Won {
    BOOL didLocalPlayerWin = YES;
    if (player1Won) {
        didLocalPlayerWin = NO;
    }
    if (self.gameOverBlock) {
        self.gameOverBlock(didLocalPlayerWin);
    }
}

Build and run. The game should now show you a game complete screen like the one shown below. Hurray! you now have a fully functional multiplayer networked game.

Game Over Screen

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 PlayerSprite.m:

//Add to the top of the file
#define kPlayerAliasLabelName @"player_alias"

//modify the initWithType: method
- (instancetype)initWithType:(enum PlayerSpriteType)playerType {
    
    . . .
    
    self = [super initWithTexture:[gameAtlas textureNamed:textureName]];
    if (self) {
        . . .
        
        SKLabelNode *playerAlias = [[SKLabelNode alloc] initWithFontNamed:@"Arial"];
        playerAlias.fontSize = 20;
        playerAlias.fontColor = [SKColor redColor];
        playerAlias.position = CGPointMake(0, 40);
        playerAlias.name = kPlayerAliasLabelName;
        [self addChild:playerAlias];

        _moveAnimation = [SKAction repeatActionForever:[SKAction animateWithTextures:textures timePerFrame:0.1]];
    }
    return self;
}

//Add new method at the bottom
- (void)setPlayerAliasText:(NSString*)playerAlias; {
    SKLabelNode *playerAliasLabel = (SKLabelNode*)[self childNodeWithName:kPlayerAliasLabelName];
    playerAliasLabel.text = playerAlias;
}

Then open PlayerSprite.h and declare the new method as shown below:

- (void)setPlayerAliasText:(NSString*)playerAlias;

With the above code you have added a label to PlayerSprite which will be displayed above the character. Also you’ve added a convenience method to set the text on that label.

Next, make the following changes to the MultiplayerNetworking class as shown below:

//Add to MultiplayerNetworkingProtocol
- (void)setPlayerAliases:(NSArray*)playerAliases;

//Add to MultiplayerNetworking.m
- (void)processPlayerAliases {
    if ([self allRandomNumbersAreReceived]) {
        NSMutableArray *playerAliases = [NSMutableArray arrayWithCapacity:_orderOfPlayers.count];
        for (NSDictionary *playerDetails in _orderOfPlayers) {
            NSString *playerId = playerDetails[playerIdKey];
            [playerAliases addObject:((GKPlayer*)[GameKitHelper sharedGameKitHelper].playersDict[playerId]).alias];
        }
        if (playerAliases.count > 0) {
            [self.delegate setPlayerAliases:playerAliases];
        }
    }
}

//Modify the tryStartGame method
- (void)tryStartGame
{
    if (_isPlayer1 && _gameState == kGameStateWaitingForStart) {
        . . .
        [self processPlayerAliases];
    }
}

//Modify the case that handles the game begin message in match:didReceiveData:fromPLayer:
else if (message->messageType == kMessageTypeGameBegin) {
    . . .
    [self processPlayerAliases];
}

Basically when the game is transitioning to the Active state, you know for sure you’ve received the player names and the players have been ordered as per the random numbers they’ve generated. So that’s a good time to set up the tell the delegate to display player aliases.

You now need to implement the setPlayerAliases: method in the GameScene class. Add the following code to GameScene.m:

- (void)setPlayerAliases:(NSArray*)playerAliases {
    [playerAliases enumerateObjectsUsingBlock:^(NSString *playerAlias, NSUInteger idx, BOOL *stop) {
        [_players[idx] setPlayerAliasText:playerAlias];
    }];
}

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

Exercise: Supporting Invites

If you’ve got this far then you must have gotten the hang of how Game Center APIs work and how to design your game to work with them. As a last feature you’re going to add support for invites.

While working with the matchmaker view you must have noticed that it gives you an option to send an invite to a friend. Right now when you select this all that will happen is that a notification will be shown on your friend’s device, when he/she selects the notification it will simply launch the game and nothing else. Ideally the game should start and create a match with the player who sent the invite.

Doing this is very simple, hence you’re going to implement this on your own. I’ve listed the steps you need to follow to get this working:

  1. Implement the GKLocalPlayerListener protocol and set the GameKitHelper class as a delegate using the registerListener: method of GKLocalPlayer. This should be done as soon as the local player is authenticated.
  2. Implement the player:didAcceptInvite: method and the player:didRequestMatchWithPlayers: method of the GKLocalPlayerListener protocol.
  3. The methods mentioned in step 2 will be invoked when the user receives an invite. Store the details you receive here in an instance variable.
  4. Since you’ve store the details of the invite received, modify the findMatchWithMinPlayers:maxPlayers:viewController:delegate: method to use these details. Use the initWithInvite: method of the GKMatchMakerViewController to initiate a match with the player who sent the invite.

Where To Go From Here?

Here is the final project with the completed SpriteKit and Game Center multiplayer networked game you created in this tutorial series. I hope this tutorial gives you a good point to start in creating amazing multiplayer games.

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 chapters on Game Center in our book iOS Games by Tutorials.

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!

Ali Hafizji

Contributors

Ali Hafizji

Author

Over 300 content creators. Join our team.