Cocos2D Tutorial for iOS: How To Create A Mole Whacking Game: Part 2/2

A Cocos2D tutorial on how to create a fun mole whacking game, that is a universal app for the iPhone and iPad. By Ray Wenderlich.

Leave a rating/review
Save for later
Share

Contents

Hide contents

Whack that laugh off this mole's face!

Whack that laugh off this mole's face!

This article is the second part of a two-part series on how to create a mole whacking 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 Cocos2D 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!