Wednesday, January 19, 2011

How To Create A Mole Whacking Game with Cocos2D: Part 2/2

How To Create A Mole Whacking Game with Cocos2D: Part 2/2

Whack that laugh off this mole's face!
Whack that laugh off this mole's face!
This article is the second part of a 2 part series on how to create a mole whacknig game with Cocos2D. This series brings together a lot of concepts from other cocos2D tutorials on this site, and introduces some new concepts along the way as well.
In the first part of the series, we created the basics of the game – cute little moles popping out of holes. We spent a lot of time thinking about how to organize the art and coordinates so that the game would look good on the iPhone, iPad, and Retina display – and be efficient too!
In this article, we’ll add some cute animations to the mole as he laughs and gets whacked, add gameplay so you can do the whacking and earn points, and of course add some gratuitous sound effects as usual.
If you don’t have it already, grab a copy of the project where we left things off in the last tutorial.

Defining Animations: Practicalities

To make the game a little more fun, we’re going to give the mole two animations. First, he’ll laugh a little when he pops out of the hole (to make you really want to whack him!), then if you do manage to whack him he’ll make a “just got whacked” face.
But before we begin, let’s talk about the practicalities of defining our animations in code.
Recall from the cocos2d animations tutorial that one of the steps to create an animation is to create a list of sprite frames. So for each different image in your animation, you have to add the sprite frame for that sprite into an array like this:
[animFrames addObject:
    [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"myImage.png"]];
Our mole’s laugh animation is going to be these images in this order: mole_laugh1.png, mole_laugh2.png mole_laugh3.png, mole_laugh2.png, mole_laugh3.png, mole_laugh1.png.
So we could hard-code a bunch of lines to set up our animation, like this:
[animFrames addObject:
    [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh1.png"]];
[animFrames addObject:
    [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh2.png"]];
[animFrames addObject:
    [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh3.png"]];
[animFrames addObject:
    [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh2.png"]];
// And so on...
But that would make our code kind of ugly. To make things a bit cleaner, instead of defining the images in the animation in code, we’ll bring them out to a property list instead.

Property Lists

If you haven’t used property lists before, they are special files you can create in XCode to contain data like arrays, dictionaries, strings, numbers, and so on in a hierarchial format. It’s extremely easy to create these, and just as easy to read them from code.
Let’s see what I mean by trying this out in XCode. Right click on Resources, choose “Add\New File…”, choose “Mac OS X\Resource\Property List”, and click “Next”. Name the new file “laughAnim.plist”, and click Finish. At this point the property list editor for laughAnim.plist should be visible, as shown below:
XCode's Property List Editor
Every property list has a root element. This is usually either an array or a dictionary. This property list is going to contain an array of image names that make up the laugh animation, so click on the second column for the root element (Type, currently set to Dictionary), and change it to Array.
Next, click the small button to the far right that looks like three lines – this adds a new entry to the array. By default, the type of the entry is a String – which is exactly what we want. Change the value to “mole_laugh1.png” for the first entry in the animation.
Click the + button to add a new row, and repeat to add all of the frames of the animation, as shown below:
Setting up Laugh Animation in Property List Editor
Next, repeat the process for the animation to play when the mole is hit. Follow the same steps as above to create a new property list named hitAnim.plist, and set it up as shown below:
Setting up Hit Animation in Property List Editor
Now, time to add the code to load these animations. Start by opening up HelloWorldScene.h and add a member variable for each animation, as shown below:
// Inside @interface HelloWorld
CCAnimation *laughAnim;
CCAnimation *hitAnim;
These will be used to keep a handy reference to each CCAnimation so it can be easily found and reused in the code.
Next add a method to create a CCAnimation based on the images defined in the property list, as follow:
- (CCAnimation *)animationFromPlist:(NSString *)animPlist delay:(float)delay {
 
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:animPlist ofType:@"plist"]; // 1
    NSArray *animImages = [NSArray arrayWithContentsOfFile:plistPath]; // 2
    NSMutableArray *animFrames = [NSMutableArray array]; // 3
    for(NSString *animImage in animImages) { // 4
        [animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:animImage]]; // 5
    }
    return [CCAnimation animationWithFrames:animFrames delay:delay]; // 6
 
}
This is important to understand, so let’s go through it line by line.
  1. The property list is included in the project file, so it’s in the app’s “main bundle”. This helper method gives a full path to a file in the main bundle, which you’ll need to read in the property list.
  2. To read a property list, it’s as easy as calling a method on NSArray called arrayWithContentsOfFile and passing in the path to the property list. It will return an NSArray with the contents (a list of strings for the image names in the animatoin, in this case). Note this works because we set the root element to be an NSArray. If we had set it to a NSDictionary, we could use [NSDictionary dictionaryWithContentsOfFile...] instead.
  3. Creates an empty array that will store the animation frames.
  4. Loops through each image name in the array read from the property list.
  5. Gets the sprite frame for each image and adds it to the array.
  6. Returns a CCAnimation based on the array of sprite frames.
Next, add the code to the end of your init method to call this helper function for each animation:
laughAnim = [self animationFromPlist:@"laughAnim" delay:0.1];        
hitAnim = [self animationFromPlist:@"hitAnim" delay:0.02];
[[CCAnimationCache sharedAnimationCache] addAnimation:laughAnim name:@"laughAnim"];
[[CCAnimationCache sharedAnimationCache] addAnimation:hitAnim name:@"hitAnim"];
Note that after squirreling away a reference to the animation, it adds it to the animation cache. This is important to do so that the animations are saved off (and retained) somewhere. It’s also helpful since you could retrieve them from the animation cache by name if you wanted (but we dont’ need to since we’re keeping a reference ourselves).
One last step – let’s use the animations (just the laugh one for now). Modify the popMole method to read as the following:
- (void) popMole:(CCSprite *)mole {          
    CCMoveBy *moveUp = [CCMoveBy actionWithDuration:0.2 position:ccp(0, mole.contentSize.height)];
    CCEaseInOut *easeMoveUp = [CCEaseInOut actionWithAction:moveUp rate:3.0];
    CCAction *easeMoveDown = [easeMoveUp reverse];
    CCAnimate *laugh = [CCAnimate actionWithAnimation:laughAnim restoreOriginalFrame:YES];
 
    [mole runAction:[CCSequence actions:easeMoveUp, laugh, easeMoveDown, nil]];      
}
The only difference here is that instead of delaying a second before popping down, it runs a CCAnimate action instead. The CCAnimate action uses the laughAnim set up earlier, and sets resotreOriginalFrame to YES so that when the animation is done, it reverts back to the normal mole face.
Compile and run your code, and now when the moles pop out, they laugh at you!
Mole with Laugh Animation
Time to wipe that smile off their faces and start whacking!

Adding Game Logic

We’re now going to add the gameplay logic into the game. The idea is a certain number of moles will appear, and you get points for each one you whack. You try to get the most number of points you can.
So we’ll need to keep track of the score, and also display it to the user. And when the moles are finished popping, we’ll need to tell the user about that as well.
So start by opening HelloWorldScene.h, and add the following instance variables to the HelloWorld layer:
CCLabelTTF *label;
int score;
int totalSpawns;
BOOL gameOver;
These will keep track of the score label, the current score, the number of moles popped so far, and whether the game is over or not.
Next add the following to the end of your init method:
self.isTouchEnabled = YES;
 
float margin = 10;
label = [CCLabelTTF labelWithString:@"Score: 0" fontName:@"Verdana" fontSize:[self convertFontSize:14.0]];
label.anchorPoint = ccp(1, 0);
label.position = ccp(winSize.width - margin, margin);
[self addChild:label z:10];
This first sets the layer as touch enabled, since you’ll want to detect when the player taps the screen. It then creates a label to show the score. Note that it sets the anchor point o the bottom right of the label so that it’s easy to place it in the lower right of the screen.
Also note that rather than pasing the font size directly, it goes through a helper function to convert the font size first. This is because the font size will need to be larger on the iPad, since it has a bigger screen. So implemenet convertFontSize next as the following:
- (float)convertFontSize:(float)fontSize {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return fontSize * 2;
    } else {
        return fontSize;
    }
}
This is very simple – on the iPad the font size is doubled, otherwise it’s left alone.
Next we want to add the touch detection code to see if a touch has hit a mole. But before we can do that, we need to add a flag to the mole to the game knows whether the mole is currently tappable or not. The mole should only be able to be tapped while it’s laughing – while it’s moving or underground it’s “safe.”
We could create a subclass of CCSprite for the mole to keep track of this, but because we only need to store this one piece of information, we’ll use the userData property on the CCSprite instead. So add two helper methods and modify popMole one more time to do this as follows:
- (void)setTappable:(id)sender {
    CCSprite *mole = (CCSprite *)sender;    
    [mole setUserData:TRUE];
}
 
- (void)unsetTappable:(id)sender {
    CCSprite *mole = (CCSprite *)sender;
    [mole setUserData:FALSE];
}
 
- (void) popMole:(CCSprite *)mole {
 
    if (totalSpawns > 50) return;
    totalSpawns++;
 
    [mole setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_1.png"]];
 
    // Pop mole
    CCMoveBy *moveUp = [CCMoveBy actionWithDuration:0.2 position:ccp(0, mole.contentSize.height)];
    CCCallFunc *setTappable = [CCCallFuncN actionWithTarget:self selector:@selector(setTappable:)];
    CCEaseInOut *easeMoveUp = [CCEaseInOut actionWithAction:moveUp rate:3.0];
    CCAnimate *laugh = [CCAnimate actionWithAnimation:laughAnim restoreOriginalFrame:YES];
    CCCallFunc *unsetTappable = [CCCallFuncN actionWithTarget:self selector:@selector(unsetTappable:)];    
    CCAction *easeMoveDown = [easeMoveUp reverse];
 
    [mole runAction:[CCSequence actions:easeMoveUp, setTappable, laugh, unsetTappable, easeMoveDown, nil]];  
 
}
The changes to popMole are as follows:
  • Right before the mole laughs, it runs a CCCallFunc action to call a specified method (setTappable). This method sets the userData property on the sprite to TRUE, which we’ll use to indicate whether the mole is tappable.
  • Similarly, after the mole laughs, it uses a CCCAllFunc action to call unsetTappable, which sets the flag back to FALSE.
  • The method also immediately returns if there has been 50 or more spawns, since 50 is the limit for this game.
  • It resets the display frame of the sprite to the base image (“mole_1.png”) at the beginning of the method, since if the mole was hit last time, it will still be showing the “hit” image and will need to be reset.
Ok, now that the sprite has a userData flag indicating whether it can be tapped or not, we can finally add the tap detection code as follows:
-(void) registerWithTouchDispatcher
{
 [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:NO];
}
 
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{ 
    CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
    for (CCSprite *mole in moles) {
        if (mole.userData == FALSE) continue;
        if (CGRectContainsPoint(mole.boundingBox, touchLocation)) {
 
            mole.userData = FALSE;            
            score+= 10;
 
            [mole stopAllActions];
            CCAnimate *hit = [CCAnimate actionWithAnimation:hitAnim restoreOriginalFrame:NO];
            CCMoveBy *moveDown = [CCMoveBy actionWithDuration:0.2 position:ccp(0, -mole.contentSize.height)];
            CCEaseInOut *easeMoveDown = [CCEaseInOut actionWithAction:moveDown rate:3.0];
            [mole runAction:[CCSequence actions:hit, easeMoveDown, nil]];
        }
    }    
    return TRUE;
}
The registerWithTouchDispatcher method sets things up so that the ccTouchBegan method gets called for each touch. For more details on this and why this is useful, check out an explanation in the How To Make a Tile Based Game with Cocos2D Ttutorial.
The ccTouchBegan method converts the touch to coordinates in the layer, and loops through each mole. If the mole isn’t tappable (the userData is false), it skips to the next mole. Otherwise, it uses CGRectContainsPoint to see if the touch point is within the mole’s bounding box.
If the mole is hit, it sets the mole as no longer tappable, and increases the score. It then stops any running actions, plays the “hit” animation, and moves the mole immediately back down the hole.
One final step – add some code to update the score and check for the level complete condition at the beginning of tryPopMoles:
if (gameOver) return;
 
[label setString:[NSString stringWithFormat:@"Score: %d", score]];
 
if (totalSpawns >= 50) {
 
    CGSize winSize = [CCDirector sharedDirector].winSize;
 
    CCLabelTTF *goLabel = [CCLabelTTF labelWithString:@"Level Complete!" fontName:@"Verdana" fontSize:[self convertFontSize:48.0]];
    goLabel.position = ccp(winSize.width/2, winSize.height/2);
    goLabel.scale = 0.1;
    [self addChild:goLabel z:10];                
    [goLabel runAction:[CCScaleTo actionWithDuration:0.5 scale:1.0]];
 
    gameOver = true;
    return;
 
}
That’s it! Compile and run your code, and you should be able to whack moles and increase your score! How high of a score can you get?
Showing the score in the game

Gratuitous Sound Effects

As usual, let’s add even more fun to the game with some zany sound effects. Download these sound effects I made with Garage Band and Audacity, unzip the file, and drag the sounds to your Resources folder. Make sure that “Copy items into destination group’s folder” is selected, and click Add.
Then make the following changes to HelloWorldScene.m:
// Add to top of file
#import "SimpleAudioEngine.h"
 
// Add at the bottom of your init method
[[SimpleAudioEngine sharedEngine] preloadEffect:@"laugh.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:@"ow.caf"];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"whack.caf" loop:YES];
 
// Add at bottom of setTappable
[[SimpleAudioEngine sharedEngine] playEffect:@"laugh.caf"];
 
// Add inside ccTouchBegan, inside the CGRectContainsPoint case
[[SimpleAudioEngine sharedEngine] playEffect:@"ow.caf"];
Compile and run your code, and enjoy the groovy tunes!

Where To Go From Here?

Here is a sample project with all of the code we’ve developed so far in this tutorial series.
That’s it for this article series (at least for now) – but if you want, why not play with this project some more yourself? I’m sure you can come up with some good ideas for how to make things even better!
I’m curious to hear if you guys think this kind of tutorial series is helpful (making a complete game to reinforce concepts covered earlier and introduce a few more along the way), or if you guys prefer normal “concept” tutorials. So if you have any thoughts or this, or any other comments, please chime in below! :]

How To Create A Mole Whacking Game with Cocos2D: Part 1/2

How To Create A Mole Whacking Game with Cocos2D: Part 1/2

Whack this Mole!
Whack this Mole!
One of the students in the iOS Programming 101 workshop that Shawn Grimes and I recently offered requested that I write a tutorial on how to write a mole whacking game with Cocos2D.
I thought this was a great idea for a tutorial for three reasons:
  1. We’ve had a lot of Cocos2D tutorials on this site, but it might be hard to see how to combine everything together to make a game. So this tutorial does exactly that!
  2. It will be a good opportunity to cover a new topic: how to make a game that works on the iPhone, iPad, and iPhone retina display.
  3. And of course the most important reason – whacking moles is fun!
This tutorial builds on the following Cocos2D tutorials:
If you have not reviewed these tutorials already (or have similar knowledge), I recommend you go through them first.
This is a two-part tutorial series. In this first part, we’ll create the basics of the game – cute little moles popping out of holes. We’ll spend a lot of time thinking about how to organize the art and coordinates so that the game looks good on the iPhone, iPad, and Retina display – and be efficient too!

Planning the Art: Overview

Since we want this app to work on both the normal iPhone, retina-display iPhone, and the iPad, we need to take some time to carefully plan out how the art is going to be set up before we proceed any further.
In order to understand how to propery size and set up the art, we need to cover three topics first:
  • Retina Display and UIKit
  • Retina Display and Cocos2D
  • iPad, iPhone, and Aspect Ratios
So let’s get started!

Retina Display and UIKit

The difference between a normal iPhone and a retina-display iPhone is the Retina display can show double the pixels. So (in landscape) instead of the display size being 480×320 px as it is on a normal iPhone, it’s 960×640 px on a Retina display.
iPhone vs Retina Display
“But wait a minute”, you may think, “wouldn’t doubling the number of pixels break all of the apps that were written assuming the 480×320 display size?” It would have (especially since when you’re programming with UIKit it’s common to hard-code frame sizes etc), except when you specify frame sizes in UIKit, you’re actually setting the sizes in points, not pixels.
On a normal iPhone, a point is defined to be exactly one pixel. But on a retina-display iPhone, a point is defined to be two pixels. So when you specify a location as (10,10) in points, it will be (10,10) on a normal iPhone, and (20,20) on a retina-display iPhone, so will appear to be at the same relative offset. Cool, eh?
When you’re using Apple’s controls or Core Graphics, Apple has already written the code to make things look nice and crisp on the Retina display.
The only trick comes into play when you use an image. Say you have a 200×200 image in an iPhone app. If you don’t do anything, on the Retina display it will just scale the image to be 2x larger – which won’t look that great because you aren’t taking advantage of the extra resolution available to you.
Zoomed image not saved in HD resolution
So what you need to do is provide another version for all of your images: a normal version, and one that is double the size. If you name your image with double the size with an “@2x” extension, whenever you try to load an image with [UIImage imageNamed:...] or similar APIs, it will automatically load the @2x image instead on the Retina display.
So making a UIKit app use the Retina display is pretty easy – just add @2X images and you’re done for the most part.
But what about Cocos2D?

Retina Display and Cocos2D

Well, there’s good news – the latest version of Cocos2D contains full support for the retina display, and makes it as easy as 1-2-3!
  1. Call enableRetinaDisplay on CCDirector to enable retina display support when your app starts up. If you’re using the Cocos2D project templates, you can just uncomment the lines that do this in your app delegate.
  2. Add double-sized sprites to your app, but instead of using the “@2x” extension, you use an “-hd” extension for Cocos2D. When loading your sprites, use hte normal name (without the “-hd” extension) – Cocos2D will automatically load the hd images on the Retina display.
  3. Now you can use points instead of pixels when positioning your sprites in Cocos2D. Note some APIs (but not many of them) still have to deal with pixels – when that is the case, they will have pixel in the method name to make it clear, otherwise assume points.
When the rubber hits the road, the easiest thing to do is to have your artist make images at the highest-necessary resolution (i.e. the 2X size for the Retina display), and you can easily scale down the images for the normal iPhone yourself from there.
You might wonder why even bother having two different sized images – why not just always load the bigger image and just scale it programatically? Well, loading textures into memory is one of the most memory intensive aspects of an app, so if you’re running on a device that isn’t going to take advantage of the higher resolution images, it’s a big saving to load the smaller images that are intended for the device.
But don’t worry – you don’t need to be constantly scaling images down in Photoshop. Texture Packer actually has a nice feautre that makes it easy to create scaled down images given a full-resolution image, and that’s what we’ll be using in this tutorial.

iPad, iPhone, and Aspect Ratio

OK so dealing with the retina display is pretty easy, but what about the iPad?
Well, it turns out that there is a very annoying thing about making a game that works on both the iPhone and the iPad – the aspect ratio between the devices is different!
The iPhone is 480×320 or 960×640 – a 1.5 aspect ratio. However, the iPad is 768×1024 – a 1.33 aspect ratio.
This means that if you have an image that fills up the entire background of the iPad (768×1024) and want to re-use it for the iPhone too, it’s not going to fit exactly. Say you scale it down so it fits the width of the iPhone (multiply by 0.9375): you’ll get 720×960, so there will be extra stuff to the side that will get cut off!
Aspect Ratio of iPhone vs. iPad
This makes things kind of annoying, because not only do you run into problems with background images, but the aspect ratio also makes it difficult to use the same coordinates across devices.
There are several strategies for how to deal with this, here are a few I’ve seen/heard/used (feel free to chime in with any of your own solutions in the comments):
  • Have a “playable area” in the middle of the screen that is the size of the iPhone retina display (640×960). This will leave a little extra are around the edges – you can just cover that with a background and the player probably won’t even notice. This allows you to easily convert coordinates between devices and re-use art (high res used on iPad and retina, and normal res used on normal iPhone). This is what we’ll be doing in this tutorial.
  • You can make the iPad have a similar aspect ratio to the iPhone if you take 42 pixel gutters on each side of the iPad screen, and put the “main content” inside as 684×1024. If you make your content to fit within the 684×1024 box, you can scale down images for each device from there.
  • You could have different images for the iPhone, iPad, and Retina display (i.e. 3 sets) and different coordinates too. This allows maximum flexibility, but larger binary sizes and having to redo the positions of objects on different devices.
On and another complication – Cocos2D doesn’t currently have any helpful support for the iPad, in terms of loading images with the “-hd” extension automatically, converting coordinates, etc. That is up to you!

Planning the Art: Conclusion

OK, so based on the above discussion, here is the plan for this tutorial.
  • The art has been designed to be within a 960×640 “playable area”, used full-screen on retina-display iPhones, and centered in the iPad screen.
  • The art will then be scaled by Texture Packer to be 1/2 the size for normal iPhones.
  • The full sized-art will be named with the “-hd” extension, and the half size without, and Cocos2D will load the appropriate art based on whether the Retina display is enabled.
  • Backgrounds are a special case because they need to be fullscreen always. The backgrounds will be made to the 1024×768 size (iPad size) so the entire screen is filled. The same images will actually be used on the iPhone too since it’s close enough. Some of the background will be offscreen, but that doesn’t matter for this particular background.
  • The iPad version will contain code to use the “-hd” images, convert coordinates to inside the “playable area”, use the appropriate font sizes, etc.
Go ahead and download the art for this tutorial, made by my lovely wife. Unzip the file and take a look at how things are set up:
  • In the “foreground” folder, the foreground is 1024×768 (the size of the iPad), but it is actually split into two parts: the lower part, and the upper part. It’s split into two parts so we can place the mole in-between the lower and upper parts, to make him look like he’s going underground.
  • In the “background” folder, the background has the 1.33 aspect ratio of the iPad, but is actually half sized (512×384). This is becase the background barely shows (just through the three mole holes), so it’s not worth the cost of a large 1024×1024 texture load. Instead a small texture is loaded and scaled up.
  • In the “sprites” folder, all sprites were sized to fit nicely within the 960×640 “playable area”. Note there’s a mole, and two animations for him (the mole laughing, and the mole being hit).
Ok – enough background info – it’s time to get started!

Getting Started

Open up XCode, select “File\New Project…” from the main menu, choose “User Templates\cocos2d\cocos2d Application”, and click “Choose…”. Name the project WhackAMole, and click Save.
Next, take the “Art” folder that you downloaded earlier and copy it into your WhackAMole project directory using Finder. It should be a sibling to the build and Classes folders, as you can see below:
Directory Structure for Project
Next, make sure you have Texture Packer installed and ready to go on your machine. If you don’t have it already or know how to use it, check out this tutorial for more information.
You will now set up TexturePacker to create the sprite sheets you’ll need for this project. You’ll be doing everything by TexturePacker’s command line tools and XCode integration, so no need to use the TexturePacker GUI at all!
Right click on Resources, choose “Add\New File…”, choose Mac OS X\Other\Shell Script, and click Next. Name the file PackTextures.sh, and click Finish.
Then replace the contents of PackTextures.sh with the following:
#!/bin/sh

TP="/usr/local/bin/TexturePacker"

if [ "${ACTION}" = "clean" ]
then
    echo "cleaning..."

    rm resources/background*
    rm resources/foreground*
    rm resources/sprites*

else
    echo "building..."

    ${TP} --smart-update \
          --format cocos2d \
          --data resources/background-hd.plist \
          --sheet resources/background-hd.pvr.ccz \
          --dither-fs \
          --opt RGB565 \
          Art/background/*.png

    ${TP} --smart-update \
          --format cocos2d \
          --data resources/background.plist \
          --sheet resources/background.pvr.ccz \
          --dither-fs \
          --scale 0.5 \
          --opt RGB565 \
          Art/background/*.png

    ${TP} --smart-update \
          --format cocos2d \
          --data resources/foreground-hd.plist \
          --sheet resources/foreground-hd.pvr.ccz \
          --dither-fs-alpha \
          --opt RGBA4444 \
          Art/foreground/*.png

    ${TP} --smart-update \
          --format cocos2d \
          --data resources/foreground.plist \
          --sheet resources/foreground.pvr.ccz \
          --dither-fs-alpha \
          --scale 0.5 \
          --opt RGBA4444 \
          Art/foreground/*.png

    ${TP} --smart-update \
          --format cocos2d \
          --data resources/sprites-hd.plist \
          --sheet resources/sprites-hd.pvr.ccz \
          --dither-fs-alpha \
          --opt RGBA4444 \
          Art/sprites/*.png

    ${TP} --smart-update \
          --format cocos2d \
          --data resources/sprites.plist \
          --sheet resources/sprites.pvr.ccz \
          --dither-fs-alpha \
          --scale 0.5 \
          --opt RGBA4444 \
          Art/sprites/*.png

fi
exit 0
This script runs TexturePacker to create a sprite sheets for the background image, the foreground images, and the sprite images – an HD and regular-quality image for each.
Note that each image is saved in the pvr.ccz format since it is the most efficient in terms of memory and disk space usage. Also the pixel format and dithering options were chosen to get a good tradeoff of quality and memory usage for each set of images.
If you’re unsure what the TexturePacker options do, load up Terminal and run TexturePacker –help to get a full description of each option.
Next, you need to set up your project to run this shell script when you compile. Right click on Targets, choose “Add\New Target…”, and choose “External Target” (not Shell Script Target!), and click Next. Name the Target TexturePacker, and click Finish.
Then double click on your TexturePacker target and set up the settings as follows:
Texture Packer Target Settings
The final step is to set this target as a dependency of your app. Double click on your TextureFun target, go to the General tab,click the + button in Direct Dependencies, choose Texture Packer from the list, and click Add Target.
Project Dependencies
Compile your app, and you should see the output from Texture Packer from your build results if everything is working OK.
Texture Packer Results
Next, add the newly generated sprite sheets and property lists to your project. Right click on Resources and choose “Add\Existing Files…” and select the background, foreground, and sprite files (12 files total) from Resources and click Add, and then Add again. When you’re done your Groups & Files tree should look similar to the following:
Project with Sprite Sheets Added
If you’d like, you can double click on any of the .pvr.ccz files to load up a preview of what’s inside. This is a new (and handy) feature of Texture Packer!

Setting the Background

Next, open up HelloWorldScene.m and find your init method. Remove the four lines of code that create the Hello World label, and replace those lines with the following:
// Determine names of sprite sheets and plists to load
NSString *bgSheet = @"background.pvr.ccz";
NSString *bgPlist = @"background.plist";
NSString *fgSheet = @"foreground.pvr.ccz";
NSString *fgPlist = @"foreground.plist";
NSString *sSheet = @"sprites.pvr.ccz";
NSString *sPlist = @"sprites.plist";
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    bgSheet = @"background-hd.pvr.ccz";
    bgPlist = @"background-hd.plist";
    fgSheet = @"foreground-hd.pvr.ccz";
    fgPlist = @"foreground-hd.plist";
    sSheet = @"sprites-hd.pvr.ccz";
    sPlist = @"sprites-hd.plist";            
}
 
// Load background and foreground
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:bgPlist];       
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:fgPlist];
 
// Add background
CGSize winSize = [CCDirector sharedDirector].winSize;
CCSprite *dirt = [CCSprite spriteWithSpriteFrameName:@"bg_dirt.png"];
dirt.scale = 2.0;
dirt.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:dirt z:-2]; 
 
// Add foreground
CCSprite *lower = [CCSprite spriteWithSpriteFrameName:@"grass_lower.png"];
lower.anchorPoint = ccp(0.5, 1);
lower.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:lower z:1];
 
CCSprite *upper = [CCSprite spriteWithSpriteFrameName:@"grass_upper.png"];
upper.anchorPoint = ccp(0.5, 0);
upper.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:upper z:-1];
 
// Add more here later...
OK let’s go over this section by section, since there is a good amount of new material here.
  • Determine names of sprite sheets and plists to load. This section lists the names of the sprite sheets and plists generated by Texture Packer to be loaded. Note that on the iPhone, Cocos2D will automatically choose between the “-hd” versions and the normal versions based on whether the Retina display is enabled. However, the iPad won’t load the “-hd” version unless you tell it to, so we check if it’s an iPad and use the “-hd” versions if so.
  • Load background and foreground. The next step is to load the information about each sprite for the background and foreground into the sprite frame cache so that they can be used later. Note that these sprites won’t actually be added to a CCSpriteBachNode anywhere – since these images are just used once each it would be kind of pointless.
  • Add background. The background image is added as a child of the layer (with a z of -2 so it appears beneath everything else) next. It scales the image by 2 because we made it smaller on purpose to conserve space, and centers the image.
  • Add foreground. The foreground is added in two parts. As an easy way to place the image, it sets the anchor point to the middle/bottom for the top image, and the middle/top for the bottom image, and matches that anchor point up to the center of the screen. That way you don’t have to do any complicated math, and it shows up in the right place on all devices. Note that part of the background will be offscreen for iPhones, but that is OK for this background and barely even noticable. Also note that the images are added with different z values, so the lower image appears on top.
Compile and run the code, and you should now see the background and foreground on the screen! Give it a try on both the iPhone and iPad simulators to make sure that it appears OK on both devices.
Mole Background
If you try the code on the retina display and zoom in, however, you’ll notice that it’s still using the normal artwork:
HD vs Normal Images in Cocos2D
That’s because we still haven’t done step 1 from the “Retina Display and Cocos2D” section earlier: call enableRetinaDisplay on CCDirector to enable retina display support when your app starts up.
To do this, open up WhackAMoleAppDelegate.m, and inside applicationDidFinishLaunching uncomment the following three lines:
if( ! [director enableRetinaDisplay:YES] )
    CCLOG(@"Retina Display Not supported");
Compile and run your code, and now when you try it on the retina display you should see it automatically make use of the HD files, due to Cocos2D’s built in retina display support!

Placing the Moles

For this game, you’re going to add three moles to the scene – one for each hole. The moles will usually be “underground” beneath the lower part of the grass – but occasionally they will “pop up” so you can try to whack them.
First, let’s add the moles to the level underneath each of the holes. We’ll temporarily make them appear above all the other art so we can make sure they’re in the right spot, then we’ll put them underground once we’re happy with their position.
Open up HelloWorldScene.h and add an array to keep track of the moles in the level, as shown below:
// Inside @interface HelloWorld
NSMutableArray *moles;
By storing the moles in this array, it will make it easy to loop through each of the moles later on.
Next, add the code to place the moles at the end of your init method (where it says “Add more here later…”), as shown below:
// Load sprites
CCSpriteBatchNode *spriteNode = [CCSpriteBatchNode batchNodeWithFile:sSheet];
[self addChild:spriteNode z:999];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:sPlist];      
 
moles = [[NSMutableArray alloc] init];
 
CCSprite *mole1 = [CCSprite spriteWithSpriteFrameName:@"mole_1.png"];
mole1.position = [self convertPoint:ccp(85, 85)];
[spriteNode addChild:mole1];
[moles addObject:mole1];
 
CCSprite *mole2 = [CCSprite spriteWithSpriteFrameName:@"mole_1.png"];
mole2.position = [self convertPoint:ccp(240, 85)];
[spriteNode addChild:mole2];
[moles addObject:mole2];
 
CCSprite *mole3 = [CCSprite spriteWithSpriteFrameName:@"mole_1.png"];
mole3.position = [self convertPoint:ccp(395, 85)];
[spriteNode addChild:mole3];
[moles addObject:mole3];
This first creates a CCSpriteBatchNode for the sprites, so that drawing the three moles is done more efficiently, and adds it as a child of the layer. Note it’s setting the z value to 999 temporarily, so that the moles apepar on top so we can make sure they’re set up OK.
It then loads all of the sprite frames from the property list to the cache, so they can be pulled out later.
Then it goes through and creates a sprite for each mole, places them in the scene, and adds them to the list of moles. Note the coordinate for each mole is within the 480×320 “playable area” of the game (the size of the iPhone). For the iPad, these points will need to be converted, so it calls a helper function convertPoint which we’ll write next.
Add the following method right above the init method:
- (CGPoint)convertPoint:(CGPoint)point {    
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return ccp(32 + point.x*2, 64 + point.y*2);
    } else {
        return point;
    }    
}
This method converts a point in the “playable area” to the appropriate screen positionon the iPad. Remember that:
  • We’re using the HD graphics on the iPad, so all points are doubled.
  • We’re centering that 960×640 area in the 1024×968 iPad screen, so that leaves 32 pixel margins on the left and right, and 64 pixel margins on the top and bottom.
So this method simply does that math to give the right position on the iPad.
One more thing – before we forget, add the following lines to clean up the memory we allocated for the moles array in the dealloc method:
[moles release];
moles = nil;
Compile and run your code, and you should see the three moles happily in the scene at the correct spots! You should try the code on the iPhone, iPhone Retina, and iPad to make sure that they’re in the right spot on each device.
Moles placed in correct positions

Popping the Moles

Now that we’re sure the moles are in the right place, let’s add the code to make them pop out of their holes.
First things first – switch the z-value of 999 for the mole sprite sheet back to 0 so the moles are underground.
Once that’s done, add the following line of code to the bottom of your init method:
[self schedule:@selector(tryPopMoles:) interval:0.5];
If you haven’t seen this before, you can run the schedule method on a node to tell Cocos2D to call another method every so many seconds. In this case, we want to try popping some moles out of their holes every 1/2 second.
Next, add the implementation of tryPopMoles:
- (void)tryPopMoles:(ccTime)dt {
    for (CCSprite *mole in moles) {            
        if (arc4random() % 3 == 0) {
            if (mole.numberOfRunningActions == 0) {
                [self popMole:mole];
            }
        }
    }     
}
This method will be called every 1/2 second, and each time it will loop through each mole and give it a 1 in 3 chance of popping out of its hole. But it will only pop out if it isn’t moving already – and one easy way to check for this is to see if the numbef running actions is 0.
Finally, add the implementation of popMole:
- (void) popMole:(CCSprite *)mole {          
    CCMoveBy *moveUp = [CCMoveBy actionWithDuration:0.2 position:ccp(0, mole.contentSize.height)]; // 1
    CCEaseInOut *easeMoveUp = [CCEaseInOut actionWithAction:moveUp rate:3.0]; // 2
    CCAction *easeMoveDown = [easeMoveUp reverse]; // 3
    CCDelayTime *delay = [CCDelayTime actionWithDuration:0.5]; // 4
 
    [mole runAction:[CCSequence actions:easeMoveUp, delay, easeMoveDown, nil]]; // 5
}
This code uses some Cocos2D actions to make the mole pop out of it’s hole, pause for half a second, then pop back down. Let’s go through this line by line to make sure we’re on the same page:
  1. Creates an action to move the mole move up along the Y axis as much as the mole is tall. Since we placed the mole right below the hole, it will look right.
  2. To make the movement look more natural, it wraps the move action with a CCEaseInOut action. This causes the action to go slower at the beginning and end, as if the mole is accelerating/decellerating, as it naturally would.
  3. To create an action to move the mole move back down again, an easy way is to call the reverse method on an action, which will give its opposite.
  4. Creates an action to pause for one second after the mole pops out.
  5. Now that the actions are ready to go, it runs them on the mole in a sequence: move up, delay, and finally move down. Note it has to terminate the sequence with a nil to show it’s done.
That’s it! Compile and run the code, and you’ll see the moles happily popping out of their holes!
Moles popping out of holes

Where To Go From Here?

Here is a sample project with all of the code we’ve developed so far in this tutorial series.
Stay tuned for the next article in this series, where we’ll add some cute animations to the mole as he laughs and gets whacked, add gameplay so you can do the whacking and earn points, and of course add some gratuitous sound effects as usual.
Please add a comment below in if you have any thoughts, advice, or suggestions for future tutorials!

Friday, January 14, 2011

uiwebview

for (id subview in self._webView.subviews)
        if ([[subview class] isSubclassOfClass: [UIScrollView class]])
            ((UIScrollView *)subview).scrollEnabled = NO;
    }
   

Wednesday, January 12, 2011

UIKit is the UI framework in the IPhoneRuntime, the equivalent of AppKit for traditional OS X apps. Most of the classes are more or less equivalent to their NS- prefixed AppKit counterparts.
Warning: The information listed here may be incomplete, incorrect, or extremely misleading. If in doubt, wait until the official SDK is released.
Useful classes:

Tuesday, January 11, 2011

iPhone - when is dealloc for a viewcontroller called?

When an object's retain count hits 0 it is marked by the run time for cleanup. When the run time cleans up the object and reclaims the memory, dealloc is called giving it a chance to clean any other object references to instance variables or the like. Does that help? If you want to see it called, put
NSLog(@"%s", _cmd);
or a break point at the beginning of your dealloc method and watch the debugger/console..




The dealloc method is called on an object when it's retain count has reached zero. Retain counts are increased by one for each retain call, and reduced once for each release call. The autorelease schedules a future release call when the current NSAutoreleasePool is drained, typically at the end of an event cycle, but you can set up your own NSAutoreleasePools on memory intensive operations. (See the NSAutoreleasePool docs for details.)
What should you put into dealloc? You should put a release for each member object the object of that class retains.
A couple things make this easier. The nil object will quietly ignore any messages sent to it, so [foo release] when foo = nil is not a bug. However, releasing an object twice can cause serious issues. My (hardly unique) solution to this is to explicitly set whatever I just released to nil, whenever I release it. In fact, I put the nil assignment on the same line as the release so I can grep for "release" and find places I missed. Example:
@interface MyClass {
    Foo *foo;
    Bar *bar;
    NSInteger baz;
}
-(void)dealloc;
@end
@implementation MyClass
-(void)dealloc {
    [foo release]; foo = nil;
    [bar release]; bar = nil;
    [super dealloc];
}
@end
I'll assign nil to a variable even when that variable is about to go out of scope or the object is about to go away. Why? If another object of the same class is allocated in the same memory space after I've released this one, it guarantees there will be no dangling pointers the new object might accidentally use and make debugging a nightmare. (See also NSZombieEnabled for debugging help.)