How To Make a Simple Playing Card Game with Multiplayer and Bluetooth, Part 1

This is a post by iOS Tutorial Team member Matthijs Hollemans, an experienced iOS developer and designer. You can find him on Google+ and Twitter. Card games are quite popular on the App Store – over 2,500 apps and counting – so it’s about time that raywenderlich.com shows you how to make one! In addition, […] By Matthijs Hollemans.

Leave a rating/review
Save for later
Share

This is a post by iOS Tutorial Team member Matthijs Hollemans, an experienced iOS developer and designer. You can find him on and Twitter.

Card games are quite popular on the App Store – over 2,500 apps and counting – so it’s about time that raywenderlich.com shows you how to make one!

In addition, this monster 7-part tutorial will demonstrate how to make the game multiplayer, so you can play against your friends over Bluetooth or Wi-Fi using the peer-to-peer features of Game Kit.

Even though you’re making a game in this tutorial, you won’t be using OpenGL or a game framework like Cocos2D. Instead, you’ll be making it with nothing more than standard UIImageViews and UIView-based animation!

The reason for not using OpenGL or Cocos2D is that you don’t really need them! UIKit is fast enough for what you’re going to do here, and excels for simple card/board games like this. Most of the content on the screen is static and you’ll only be animating a few views at a time.

To follow along with this tutorial, you will need Xcode 4.3 or later. If you still have Xcode 4.2, then it’s time to upgrade!

Also, to test the multiplayer functionality, you will need at least two devices running iOS 5 or better. If you have a home Wi-Fi network, then you can make do with a single device, but ideally you’ll have more than one (I used four different devices while writing the code for this tutorial).

If you are new to this series, check out the introduction first. There you can see a video of the game, and we’ll invite you to our special Reader’s Challenge!

Then keep reading to impress your friends with the best card trick of all – your own multiplayer card game app!

Introducing: Snap!

The card game that you’ll be making is a children’s game called Snap!. This is what it looks like when it’s done:

The finished game of Snap!

In case you’re not familiar with the rules of Snap!, the game is played with 2 to 4 players using a standard 52-card deck. The goal is to win all the cards. You win cards by spotting a matching pair.

At the start of each round, the dealer shuffles the deck and deals out the cards clockwise around the table until there are none left in the deck. The cards are placed face down in front of the players.

The players take turns in clockwise order. If it’s your turn, you turn over the top card from your pile. The idea is to yell “Snap!” as quickly as possible when you see that any of the open cards form a matching pair. Two cards match if they have the same value, for example two kings. The suit does not matter.

The player who yells “Snap!” the quickest wins both piles and adds them to his own stack of face-down cards. This continues until one player has all the cards. If a player yells “Snap!” when there is no match on the table, he’ll have to pay one card to each of the other players.

The Application Flow

This is a multiplayer game that can be played over Bluetooth or Wi-Fi, and you’ll be using the Game Kit framework to make this happen. Note that you’re only going to use the peer-to-peer connectivity features of Game Kit – this app will not use Game Center at all. In fact, this tutorial restricts itself to a single class from Game Kit, GKSession.

In the first part of this tutorial, you’ll learn how to connect the players’ devices so that they can communicate over Bluetooth or Wi-Fi. With Snap! there will always be one player who hosts the game, also referred to as the “server.” The other players will join the session that this host has set up. These players are the “clients.”

The flow of the application is roughly as follows:

Mockup of the main screen

Above is the main screen of the app, and the first thing a player sees. She can decide to host a game that others can join, join a game hosted by someone else, or play a single-player game against the computer.

Mockup of the Host Game screen

The “Host Game” screen contains a table view that lists the players that have joined this session. Pressing the Start button will begin the game; from that point on, no new players can join. Usually the players decide between themselves beforehand who will host the game, and everybody else then joins that game.

Mockup of the Join Game screen

The “Join Game” screen looks very similar to the Host Game screen, except that there is no Start button. The table view lists the available games (there may very well be multiple people hosting a game). Here you tap on the game you want to join and wait until the host taps his Start button.

Mockup of the game screen

The game screen shows the players sitting around the table with their piles of face-up and face-down cards. The button in the lower-right corner lets the local player yell “Snap!” (there’s no actual need to run around the room screaming when you’re playing the iPhone game). If any of the other players yell “Snap!”, you’ll see a speech bubble next to their name.

Getting Started

To save you some time, I’ve already prepared a basic Xcode project that contains all the resources (such as images and nib files) that you’ll need for this first part. Download the starter code here and open Snap.xcodeproj in Xcode.

If you look at the source code, you’ll see that the project contains a single view controller, MainViewController. Run the app and it doesn’t look like much yet:

The starter version of the app

The main screen contains five UIImageViews for the logo cards (S, N, A, P and the joker) and three UIButtons. You can make this look a bit more lively by animating the image views, but first you’ll improve the look of those buttons.

The download also includes a file named Action_Man.ttf. This is a custom font that you’re going to use instead of the standard Helvetica, or any of the iPhone’s built-in fonts. If you double-click the Action_Man.ttf file on your Mac, it will open in Font Book:

The Action Man font in Font Book

That looks a bit more exciting than the standard fonts, if you ask me. Unfortunately, you can’t simply install this font on the Mac and then put it on your buttons in Interface Builder. You have to write some code to make this happen. First, however, you need to tell UIKit about this font, so your app can load it.

Open Snap-Info.plist in Xcode. Add a new row and give it the key “Fonts provided by application.” This is an array type. Give the first item the name of the font file, Action_Man.ttf:

Adding font to Info plist

You also need to add the actual TTF file to the project. Simply drag it into the Supporting Files section:

The TTF file added to the project

Note that you need to check the Snap target in the Add to Targets section of the dialog box that pops up. Otherwise, the font file will not be included in the application bundle:

Adding the new file to the target

Now you can simply set this font on your buttons and labels by doing:

UIFont *font = [UIFont fontWithName:@"Action Man" size:16.0f];
someLabel.font = font;

To avoid having to repeat this code over and over, make a category for it. From the File menu, select the New->File… option and choose the “Objective-C category” template. Fill in “SnapAdditions” for the category name and “UIFont” as the class that will take the category:

Creating a category on UIFont

This creates two new files, UIFont+SnapAdditions.h and UIFont+SnapAdditions.m. Just to keep things tidy, I placed these two files into a new group named Categories:

Placing the source files in the Categories group

Replace the contents of UIFont+SnapAdditions.h with:

@interface UIFont (SnapAdditions)

+ (id)rw_snapFontWithSize:(CGFloat)size;

@end

Then replace the contents of the .m file with:

#import "UIFont+SnapAdditions.h"

@implementation UIFont (SnapAdditions)

+ (id)rw_snapFontWithSize:(CGFloat)size
{
	return [UIFont fontWithName:@"Action Man" size:size];
}

@end

It’s a pretty simple category. You’ve just added a single class method, rw_snapFontWithSize:, which will create a new UIFont object with the Action Man font.

Notice that the file name of the font is Action_Man.ttf, with an underscore. But the name of the font has no underscore. You should always use the name of the font itself, not its filename. To find out the name of a font, double-click it from Finder to open it in Font Book. (Notice in the Font Book screenshot above that it does say Action Man and not Action_Man.)

The second thing to notice here is that I have prefixed the method name with “rw_”. It’s always a good idea to prefix methods that you add to categories on standard framework classes with your initials (or some other unique identifier), just to make sure the method name won’t clash with a built-in method, or a method that Apple may add in the future. It’s unlikely that Apple would actually add a method named “snapFontWithSize:”, but it’s better to be safe than sorry.

With these preparations in place, you can now set this new font on the buttons from your Main View Controller. At the top of MainViewController.m, add an import for your new category:

#import "UIFont+SnapAdditions.h"

Add the viewDidLoad method inside the @implementation block:

- (void)viewDidLoad
{
	[super viewDidLoad];

	self.hostGameButton.titleLabel.font = [UIFont rw_snapFontWithSize:20.0f];
	self.joinGameButton.titleLabel.font = [UIFont rw_snapFontWithSize:20.0f];
	self.singlePlayerGameButton.titleLabel.font = [UIFont rw_snapFontWithSize:20.0f];
}

That should do it. Run the app again. The button titles will show up in their new font:

The buttons with the new font

If you’re still seeing the old font, make sure the Action_Man.ttf file is truly being placed in the application bundle. Select the file and make sure the box in the Target Membership section is checked (in the File Inspector in the righthand pane of the Xcode window):

Target membership checkbox

Note: Be careful to read the font’s license agreement when you embed a custom TTF file into an app. Font files are protected by copyright and often require license fees if you want to distribute them as part of your app. Fortunately for you, the Action Man font is free to use and distribute.

The button titles look a lot better already, but a proper button has a border. To add borders, you’ll use a set of stretchable images. These images have already been added to the project and are named Button.png and ButtonPressed.png.

Because you have several different screens that all need to use similar-looking buttons, you’ll place the code for customizing the appearance of the buttons into a category as well.

Add a new category to the project. Again name it “SnapAdditions,” but this time, make it a category on UIButton. This creates two new files, UIButton+SnapAdditions.h and UIButton+SnapAdditions.m. Put these into the Categories group as well, and then replace the contents of the .h file with:

@interface UIButton (SnapAdditions)

- (void)rw_applySnapStyle;

@end

Replace the contents of the .m file with:

#import "UIButton+SnapAdditions.h"
#import "UIFont+SnapAdditions.h"

@implementation UIButton (SnapAdditions)

- (void)rw_applySnapStyle
{
	self.titleLabel.font = [UIFont rw_snapFontWithSize:20.0f];

	UIImage *buttonImage = [[UIImage imageNamed:@"Button"] stretchableImageWithLeftCapWidth:15 topCapHeight:0];
	[self setBackgroundImage:buttonImage forState:UIControlStateNormal];

	UIImage *pressedImage = [[UIImage imageNamed:@"ButtonPressed"] stretchableImageWithLeftCapWidth:15 topCapHeight:0];
	[self setBackgroundImage:pressedImage forState:UIControlStateHighlighted];
}

@end

Once again, there’s only one method (with the “rw_” prefix). When you call this method on a UIButton object, it will give the button a new background image, and it will also set the font.

Add an import for this new category in MainViewController.m:

#import "UIButton+SnapAdditions.h"

You can now replace viewDidLoad with the following:

- (void)viewDidLoad
{
	[super viewDidLoad];

	[self.hostGameButton rw_applySnapStyle];
	[self.joinGameButton rw_applySnapStyle];
	[self.singlePlayerGameButton rw_applySnapStyle];
}

Run the app again. Now the buttons look like real buttons:

The buttons now have borders

Contributors

Over 300 content creators. Join our team.