The game works well with the development we have covered, but there are a few places where we can add a little more visual flair and some excitement. Players love having a game that keeps score. They also love animation. There is excitement to be had by having the ability to lose. Let's give the player what they want.
We build the score and lives displays using
CCLabelTTF
labels, with the variables playerScore
and livesRemaining
as their label contents. These are declared as variables of the layer, so we can easily update them. When we
start animating tiles, it will be useful to know where the score and lives displays are on screen.
There are two main approaches to adding text to the screen:
CCLabelTTF
and CCLabelBMFont
. Both have their uses, which we will briefly outline here. CCLabelTTF
uses a standard TTF font file. The way it draws the text on the screen is not very efficient and can cause performance issues in some uses. The other approach, CCLabelBMFont
, uses a bitmap (image file) of the font and internally uses a batch node to render the text. This means it is highly efficient at drawing, with very little performance concern. Other than the use of a TTF file versus an image file, the way you code for them is very similar. One potential issue with a
BMFont
file is that you must have the entire font in a single bitmap. If you are using a large font size, this often causes you to need to leave out some characters that may be needed to support international keyboards. A TTF file does not have this problem. Also, it is common with the CCLabelBMFont
approach to have multiple versions of the font if you want to use different font sizes. In this book, we will use CCLabelTTF
labels throughout because we do not have any performance (frame rate) concerns with any of these projects.
If we were to have performance issues, we would certainly switch to using CCLabelBMFont
instead of CCLabelTTF
. We leave it as an exercise for the reader to convert these projects to use the CCLabelBMFont
class. (For creation of the bitmaps, an excellent resource is Glyph Designer, available at http://glyphdesigner.71squared.com.)
Filename: MTPlayfieldLayer.m
-(CGPoint) scorePosition { return ccp(size.width - 10 - tileSize.width/2, (size.height/4) * 3); } -(CGPoint) livesPosition { return ccp(size.width - 10 - tileSize.width/2, size.height/4); }
Rather than hardcoding the values in multiple places, it is a preferred approach to create helper methods such as scorePosition
and livesPosition
, which return a CGPoint
reference of where those elements are onscreen. Here we see the calculations, which place the score and lives near the left edge of the screen, with the score three quarters of the way up the screen, and the lives one quarter of the way up the screen.
The creation of simple labels is very basic, using the positioning we saw above. To see how the score and lives are created, please consult the accompanying code bundle for this book.
Now we will need a way to score and animate the tiles when the player makes a successful match. When a match is scored, we will fly the tiles to the score, and then have them shrink into the score position until they disappear. Let's see how that works:
Filename: MTPlayfieldLayer.m
-(void) scoreThisMemoryTile:(MTMemoryTile*)aTile { // We set a baseline speed for the tile movement float tileVelocity = 600.0; // We calculate the time needed to move the tile CGPoint moveDifference = ccpSub([self scorePosition], aTile.position); float moveDuration = ccpLength(moveDifference) / tileVelocity; // Define the movement actions CCMoveTo *move = [CCMoveTo actionWithDuration: moveDuration position:[self scorePosition]]; CCScaleTo *scale = [CCScaleTo actionWithDuration:0.5 scale:0.001]; CCDelayTime *delay = [CCDelayTime actionWithDuration:0.5]; CCCallFuncND *remove = [CCCallFuncND actionWithTarget:self selector:@selector(removeMemoryTile:) data:aTile]; // Run the actions [aTile runAction:[CCSequence actions:move, scale, delay, remove, nil]]; // Play the sound effect [[SimpleAudioEngine sharedEngine] playEffect:SND_TILE_SCORE]; // Remove the tile from the tilesInPlay array [tilesInPlay removeObject:aTile]; // Add 1 to the player's score playerScore++; // Recalculate the number of lives left [self calculateLivesRemaining]; }
Here we leverage the cocos2d actions heavily, using the stock actions of CCMoveTo
, CCScaleTo
, CCDelayTime
, and CCCallFuncND
. One aspect of our flying-to-score effect is that we want the tiles to move at a constant rate. If we hardcoded a duration for the CCMoveTo
action, the tiles closer to the score would move slowly, and those farther away would move really fast. To achieve a constant rate, we set a desired speed (tileVelocity
), then calculate how far away the tile is from the score. We divide these out to arrive at the correct movement duration for this tile. After we initiate the actions, we increment the score by one (playerScore++
), and call the
calculateLivesRemaining
method (which we will see shortly).