How To Make a Letter / Word Game with UIKit: Part 1/3

This 3-part tutorial series will guide you through the process of building a board game for the iPad, where you create words with letters. You’ll also learn about best practices for creating solid, modular iOS apps. And as a bonus, you’ll get a crash course in audio-visual effects with UIKit! By Marin Todorov.

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

5) Dealing the Tiles on the Screen

Switch back to GameController.m – your dealRandomAnagram method already loads the data you need, but it still does not do much. Time to fix that!

Add the following imports with the others at the top of GameController.m:

#import "config.h"
#import "TileView.h"

Now you’ll have access to your TileView class and any shared settings you store in config.h.

Add the following private variables to the @implementation section:

@implementation GameController
{
    //tile lists
    NSMutableArray* _tiles;
    NSMutableArray* _targets;
}

So what’s up with these variables?

  • _tiles is an array to hold the TileViews that will display the tiles at the bottom of the screen. These tiles contain the initial phrase of the anagram.
  • _targets is an array to hold the target views that will display the spaces at the top of the screen where the player has to drag tiles to form the second phrase of the anagram.

Before you continue, you also need a constant for the space between the tiles. Jump over to config.h and add the following define:

#define kTileMargin 20

Some of you might already be protesting, “Can’t I just use 20 where I need it and be done?” It’s a good point, but there is a reason for predefining in the config file.

My experience shows that you will have to tweak any numbers you have in your code when it comes time to fine-tune the gameplay. So the best practice is to externalize all numbers to a config file and give those variables meaningful names. This will pay off especially well if you are working in a team and you need to hand the game to a game designer for fine-tuning. :]

OK, back in GameController.m, get ready to add some code to the end of dealRandomAnagram. You have the number of characters in both phrases, so it’s easy to calculate what size tiles you need. Add this code at the end of the method:

//calculate the tile size
float tileSide = ceilf( kScreenWidth*0.9 / (float)MAX(ana1len, ana2len) ) - kTileMargin;

You take 90% of the width of the screen and divide it by the number of characters. You use the phrase that has more characters – remember, the number of spaces can vary! You decrease the result by kTileMargin to account for space between tiles.

Next find the initial x position of the first tile. It will depend on the length of the word and the tile size you just calculated. Once again, add the following at the end of dealRandomAnagram:

//get the left margin for first tile
float xOffset = (kScreenWidth - MAX(ana1len, ana2len) * (tileSide + kTileMargin))/2;

//adjust for tile center (instead of the tile's origin)
xOffset += tileSide/2;

You take the screen width, subtract the calculated width of the word tiles and find where the first tile should be positioned on the screen. Wouldn’t it be nice if there were a simpler way to “center” the tiles!

LeSighHisLeg

Continuing in the same method, add the following code to display the tiles:

//1 initialize tile list
_tiles = [NSMutableArray arrayWithCapacity: ana1len];

//2 create tiles
for (int i=0;i<ana1len;i++) {
    NSString* letter = [anagram1 substringWithRange:NSMakeRange(i, 1)];
    
    //3
    if (![letter isEqualToString:@" "]) {
        TileView* tile = [[TileView alloc] initWithLetter:letter andSideLength:tileSide];
        tile.center = CGPointMake(xOffset + i*(tileSide + kTileMargin), kScreenHeight/4*3);

        //4
        [self.gameView addSubview:tile];
        [_tiles addObject: tile];
    }
}

Here's what's happening above:

  1. First you initialize a fresh copy of your _tiles mutable array and ensure it is large enough to hold all of the letters in the initial phrase. It's important to start with a clear array here because this method will be called multiple times during a run of your app. Since spaces are skipped, if you didn't clear out the array then, where there should be spaces, there might instead be old tiles left over from a previous phrase.
  2. You then loop through the phrase and create a new NSString for each character. This is stored as letter.
  3. You check each letter and if it is not a space, it's a go! You create a new tile and position it using xOffset, tileSide and kTileMargin. Here you are calculating the position based on that letter's position in the phrase, which is represented by i. So if i is 5, this math will figure out how much space five tiles take up and add the new tile to the right of that space.

    You position the tiles vertically at 3/4ths of the screen height.
  4. Finally, you add the tile to the gameView and to the _tiles array. The first actually shows the tile on the screen, while the latter helps you loop faster over the tiles later on.

Alrighty! Time for the final touch – you need to load the level data and call dealRandomAnagram. Switch to ViewController.m and add these two lines at the end of viewDidLoad::

self.controller.level = level1;
[self.controller dealRandomAnagram];

Remember that you already have a Level object – you created it at the top of this method – so you just pass it on to your GameController and call dealRandomAnagram.

It's been a long time in coming, but build and run to check out the result! Your Simulator should look something like this:

Awesome, it works! It's alive!!!

Note: Your view probably won't look exactly like this because the app is choosing a random anagram each time it runs. Run it a few times to see how the app sizes the tiles differently for different phrases.

You are closing in on the finish line for the first part of this tutorial series.

6) Add the Letters

Switch to TileView.m. This is where you're going to add letters to those tiles. Find the comment in initWithLetter:andSideLength saying "//more initialization here" and just below it, add the following code:

//add a letter on top
UILabel* lblChar = [[UILabel alloc] initWithFrame:self.bounds];
lblChar.textAlignment = NSTextAlignmentCenter;
lblChar.textColor = [UIColor whiteColor];
lblChar.backgroundColor = [UIColor clearColor];
lblChar.text = [letter uppercaseString];
lblChar.font = [UIFont fontWithName:@"Verdana-Bold" size:78.0*scale];
[self addSubview: lblChar];

The above code creates a new UILabel and adds it to the tile view. Because you want the letter to be as easy to read as possible, you create the label to be as large as the tile itself. You then align the letter to show up in the center of the tile.

You ensure the letter is uppercase by calling [letter uppercaseString]. Using capital letters will ease the game play, as they are easier to recognize, specifically by children. Always think about your game's target audience!

Finally you reuse the scale factor you calculated earlier. For the full-size tile a Verdana font with char size of 78pts fits well, but for other sizes you need to scale the font size accordingly.

Add these last few lines of initialization to the end of initWithLetter:andSideLength:

//begin in unmatched state
self.isMatched = NO;

//save the letter
_letter = letter;

The above code sets the tile's isMatched flag to NO, indicating this tile has not yet been matched to a target at the top of the screen. It also saves the letter to the tile's letter property.

Build and run the project. As I said, you're now moving fast towards your goal. Good job! :]

Once again, your screen probably won't look just like this one. But that just means your game controller is doing what's it's supposed to do and dealing a random anagram. Nice!

Have another look at the image above. Right now the tiles look like they were put on the board by a pedantic robot – they are perfectly aligned. Now comes the time to add a pinch of randomization!

Contributors

Over 300 content creators. Join our team.