Beginning Turn-Based Gaming with iOS 5 Part 2

Note from Ray: This is the seventh iOS 5 tutorial in the iOS 5 Feast! This tutorial is a free preview chapter from our new book iOS 5 By Tutorials. Enjoy! This is a blog post by iOS Tutorial Team member Jacob Gundersen, an indie game developer who runs the Indie Ambitions blog. Check out […] By Jake Gundersen.

Leave a rating/review
Save for later
Share
You are currently viewing page 5 of 5 of this article. Click here to view the first page.

Ending the Game

We’ve really only got one thing left to accomplish, and that’s ending the game. But, we probably also want to give our users some advance notice that they only have a certain number of turns left.

We’ll do this with a new method that checks the length of the NSData. We’ll end the game when it gets to above 3800 characters, but let’s start letting our players know when the game gets to about 3000 characters.

This new method will be called each time a match is loaded, so we’ll put it in our takeTurn method and our layoutMatch method. If the match is getting close to being over, this method will add some information to our statusLabel, saying, there’s only about 200 characters left.

Add this method to ViewController.m right after dealloc:

-(void)checkForEnding:(NSData *)matchData {
    if ([matchData length] > 3000) {
        statusLabel.text = [NSString stringWithFormat:
          @"%@, only about %d letter left", statusLabel.text, 
            4000 - [matchData length]];
    }
}

Also call this method at the end of layoutMatch and takeTurn:

[self checkForEnding:match.matchData];

Go ahead an build and run, you should get something like this (after you have written a bunch of stuff).

Ending game based on character count

What we’ll do is end the game whenever the total character count exceeds 3800. The reason is that we don’t want the possibility that the NSData string will be larger than 4096, because Game Center won’t take the endTurn if the matchData is too large.

So, in modify the sendTurn one last time to the following:

- (IBAction)sendTurn:(id)sender {
    GKTurnBasedMatch *currentMatch = 
      [[GCTurnBasedMatchHelper sharedInstance] currentMatch];
    NSString *newStoryString;
    if ([textInputField.text length] > 250) {
        newStoryString = [textInputField.text substringToIndex:249];
    } else {
        newStoryString = textInputField.text;
    }
    
    NSString *sendString = [NSString stringWithFormat:@"%@ %@", 
      mainTextController.text, newStoryString];
    NSData *data = [sendString dataUsingEncoding:NSUTF8StringEncoding ];
    mainTextController.text = sendString;
    
    NSUInteger currentIndex = [currentMatch.participants 
      indexOfObject:currentMatch.currentParticipant];
    GKTurnBasedParticipant *nextParticipant;
    
    NSUInteger nextIndex = (currentIndex + 1) % 
      [currentMatch.participants count];
    nextParticipant = [currentMatch.participants objectAtIndex:nextIndex];
    
    for (int i = 0; i < [currentMatch.participants count]; i++) {
        nextParticipant = [currentMatch.participants 
          objectAtIndex:((currentIndex + 1 + i) % 
            [currentMatch.participants count ])];
        if (nextParticipant.matchOutcome != GKTurnBasedMatchOutcomeQuit) {
            NSLog(@"isnt' quit %@", nextParticipant);
            break;
        } else {
            NSLog(@"nex part %@", nextParticipant);
        }
    }
    
    if ([data length] > 3800) {
        for (GKTurnBasedParticipant *part in currentMatch.participants) {
            part.matchOutcome = GKTurnBasedMatchOutcomeTied;
        }
        [currentMatch endMatchInTurnWithMatchData:data 
          completionHandler:^(NSError *error) {
            if (error) {
                NSLog(@"%@", error);
            }
        }];
        statusLabel.text = @"Game has ended";
    } else {
        
        [currentMatch endTurnWithNextParticipant:nextParticipant 
          matchData:data completionHandler:^(NSError *error) {
            if (error) {
                NSLog(@"%@", error);
                statusLabel.text = 
                   @"Oops, there was a problem.  Try that again.";
            } else {
                statusLabel.text = @"Your turn is over.";
                textInputField.enabled = NO;
            }
        }];
    }
    NSLog(@"Send Turn, %@, %@", data, nextParticipant);
    textInputField.text = @"";
    characterCountLabel.text = @"250";
    characterCountLabel.textColor = [UIColor blackColor];
}

We’re just wrapping the current endTurn call in an if statement. If we are above 3800 characters, instead of calling endTurn, we’ll call endMatch. Once we call end game that notification we’ll be sent to all the other players in the handleMatchEnded notification.

We’ll deal with that in a second, but let’s give our end game a whirl. Make sure your handleEndMatch notice still has a log statement in it, and hopefully you’ve still got your last game around (didn’t it take forever to write 3000 characters?).

You should be able to get this:

A game that has ended

All right, end in sight. Now we just need to handle the handleMatchEnded in GCTurnBasedMatchHelper.m:

-(void)handleMatchEnded:(GKTurnBasedMatch *)match {
    NSLog(@"Game has ended");
    if ([match.matchID isEqualToString:currentMatch.matchID]) {
        [delegate recieveEndGame:match];
    } else {
        [delegate sendNotice:@"Another Game Ended!" forMatch:match];
    }
}

If we’re looking at another match we’ll just send the notice, that spawns the UIAlert, otherwise we’ll send recieveEndGame. Let’s implement that as well in ViewController.m:

-(void)recieveEndGame:(GKTurnBasedMatch *)match {
    [self layoutMatch:match];
}

We could do something extra in this case, but because we’re setting the statusLabel in layoutMatch this should suffice. However, should we decide later to add something to an endGame notice, like email our story or invite the same group to play again, we could do that here.

Here’s what it looks like if you get a game ended notice in while the device is locked:

A system notice when a turn based game ends

Where To Go From Here?

Here is the complete example project that we developed in the tutorial series.

The Turn-Based Gaming API is a great tool for all kinds of strategy, card, board, and other games that are played asynchronously between multiple users, so I look forward to seeing what you come up with!

To learn more about Game Center’s API, check out our new book iOS 5 By Tutorials. We have another chapter on Game Center where we cover how to customize the boring Turn-Based Gaming UI with your own interface programmatically! Not only does this look better, but it gives a better user experience because you can display additional information about the games the user is involved in.

If you have any questions or comments on this tutorial or on turn-based gaming in iOS 5 in general, please join the forum discussion below!

This is a blog post by iOS Tutorial Team member Jacob Gundersen, an indie game developer and co-founder of Third Rail Games. Check out his latest app - Factor Samurai!

Jake Gundersen

Contributors

Jake Gundersen

Author

Over 300 content creators. Join our team.