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

Ray Wenderlich

This post is also available in: Japanese

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!

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 Tutorial.

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 Cocos2D 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 Cocos2D 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! :]

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

User Comments

59 Comments

[ 1 , 2 , 3 , 4 ]
  • Why are your sprite sheets so large? (How many do you have?)

    What happens if you don't call 'popDrum'? Does that make it not freeze?
    regularberry
  • The sprite sheets are so large due to the quality of the images, they are high definition PNGs which were initially rendered in 3D. If I comment out the call to popDrum the selector continues to fire without issue. I have profiled the memory consumption with leaks and none are reported, the total memory consumed is stable at around 28MB. What is the maximum recommended memory consumption for an IPad app?

    What I have noticed is that when it freezes often the 2nd animation in the sequence - downAnimate has not run on a couple of the sprites.

    Code: Select all
        [drum.drumSprite runAction:[CCSequence actions:upAnimate, delay, downAnimate, nil]];


    I added a debug to the CCSchedule class and found that while the app appears frozen, it actually continues to fire the tick method, always entering the following block

    // updates with priority == 0
    DL_FOREACH_SAFE( updates0, entry, tmp ) {
    if( ! entry->paused && !entry->markedForDeletion )
    {
    entry->impMethod( entry->target, updateSelector, dt );
    }
    }

    I havent yet managed to understand what it is doing and how to try and tie it back to my selector.
    kenada
  • The first thing that comes to mind is that I notice you're using CCAnimationCache. The thing about CCAnimationCache is when there is a memory warning, it will flush the contents of the cache, so an animation you put in there earlier might not be there anymore.

    Instead, why not try creating a CCAnimation and retaining it in an instance variable, instead of using CCAnimationCache.
    rwenderlich
  • Great set of tutorial, but I'm running into a few snags.

    - (Not an issue)I've noticed that running in the simulator, both the iPad and the Retina version, the framerate is much lower (iPad hovers at 17.0, Retina at around 20.0) is this expected? Don't have a physical device to test on. (Got a system to test it on, not an issue)

    - (Solved) I'm also getting a weird hang once the moles pop up. I ended tracing it down to animationFromPlist not populating the array, but I don't see any difference between what I have and the example posted. I've also tried downloading the sample code, and interestingly in XCode 4, the items in the plist isn't nested under the Root, is this expected? (Solved in next post)

    - Running in Retina, I'm also getting this in my log:
    2011-07-18 16:25:53.631 WhackAMole[28110:207] cocos2d: Filename(background-hd.pvr.ccz) contains -hd suffix. Removing it. See cocos2d issue #1040
    2011-07-18 16:25:53.637 WhackAMole[28110:207] cocos2d: Filename(foreground-hd.pvr.ccz) contains -hd suffix. Removing it. See cocos2d issue #1040
    2011-07-18 16:25:53.722 WhackAMole[28110:207] cocos2d: Filename(sprites-hd.pvr.ccz) contains -hd suffix. Removing it. See cocos2d issue #1040


    Cause for concern?
    AlphaTwo
  • OK, so I solved the plist issue by doing some digging, and might be a good idea to add this to the tutorial for anyone on XCode 4.

    The part about creating the plist doesn't work exactly the same as the diagrams listed. In the editor, I had created a "root node", and added the items accordingly. This plist file wasn't being picked up by arrayWithContentsOfFile:. Doing a little searching about plist, I stumbled upon this: http://stackoverflow.com/questions/5477797/change-plist-root-type-to-array-in-xcode-4 Seems like in XCode4, the default root is "hidden" away, and always set to dictionary. The following is the XML code generated in XCode 4 when I followed the tutorial guide step by step (creating root, etc...)

    Code: Select all
    <plist version="1.0">
    <dict>
       <key>Root</key>
       <array>
          <string>mole_laugh1.png</string>
          <string>mole_laugh2.png</string>
          <string>mole_laugh3.png</string>
          <string>mole_laugh2.png</string>
          <string>mole_laugh3.png</string>
          <string>mole_laugh1.png</string>
       </array>
    </dict>
    </plist>


    contrast that with the sample code:
    Code: Select all
    <plist version="1.0">
    <array>
       <string>mole_laugh1.png</string>
       <string>mole_laugh2.png</string>
       <string>mole_laugh3.png</string>
       <string>mole_laugh2.png</string>
       <string>mole_laugh3.png</string>
       <string>mole_laugh1.png</string>
    </array>
    </plist>


    Just a little heads up for anyone attempting this in XCode 4.
    AlphaTwo
  • I removed the use of the animation cache and added the following properties to my Drum class

    Code: Select all
       
    CCAnimation *downAnimation;
    CCAnimation *upAnimation;


    My selector method tryPopDrums continues to call popDrum

    Code: Select all
    -(void)popDrum:(Drum *)drum
    {
        CCDelayTime *delay = [CCDelayTime actionWithDuration:2];   
        CCAnimate *upAnimate = [CCAnimate actionWithAnimation:drum.upAnimation restoreOriginalFrame:FALSE];
        CCAnimate *downAnimate = [CCAnimate actionWithAnimation:drum.downAnimation restoreOriginalFrame:FALSE];
        [drum.drumSprite runAction:[CCSequence actions:upAnimate, delay, downAnimate, nil]];
      }
           
    - (void)tryPopDrums:(ccTime)dt
    {
        NSLog(@"String is %@",@"pop1");
        for (Drum *drum in drums) {           
            if (arc4random() % 5 == 0)
            {
                if ([drum.drumSprite numberOfRunningActions] ==0)
                {
                       NSLog(@"String is %@",@"pop2");
                     [self popDrum:drum];
                }
            }
        }     
    }


    It gets a called a random number of times anywhere, I have seen the selector run anywhere from 8 to 15 times before everything seems to freeze with some of the sprites still having not run the downAnimate part of their sequence, popDrum is normally only called 4 or 5 times before this happens. The CCScheduler tick method is still running.
    kenada
  • Ray,

    As mentioned I have 5 drums, A-E, which are placed left right to right in an arc, with the middle drum C being closest, B and D, slight further back, and A and E at the back. I am using their natural position, rather than setting it as the images were all originally on a 1024 by 768 PNG. Drums D and E have a slight overlap, as I create E last it is actually overlapping and cutting off some of D as they were both created with the same Z position, E is meant to be at the back of the arc so I guess I should give it a lower Z position.

    To cut a lot story short, if I add an if statement in my selector method to ignore the drum call to popDrum if it is Drum D, all of the animations run without issue, E included. However if I allow drum D to run as soon as its upAnimate method runs, the animations freeze, its downAnimation is never run and the selector is never called again. I guess somewhere an error is being swallowed.

    I assigned drum E a lower Z position and it now appears behind D but the same issue exists, if animations are allowed to run on D, the selector breaks and it freezes, animations run on E without issue.

    What are the rules with running animations on sprites which have some overlap, does this resultant behaviour make sense, I am breaking a common design rule?

    Update:11:46 PM.

    One more thing I discovered is that after adding touch control if more than one drum is in the tappable state the
    Code: Select all
    if (CGRectContainsPoint(drumSprite.boundingBox , touchLocation))

    will return true for both as both of all my sprites seem to have a bounding box of 1024 by 768.

    Whats the recommended way to box and position sprites?

    Thanks
    kenada
  • @AlphaTwo Thanks for the inspiration! Came up with a fix and some house cleaning to boot!

    @Ray Tell me what you think!

    For the sake of organization I opted for one plist named "moleAnim.plist" using a dictionary as the root object (default for Xcode 4) for the mole animations. Who knows you might have an evil mole or some other variant pop up in the future!

    Screen Shot 2011-08-22 at 12.05.01 PM.png


    First replace - (CCAnimation *)animationFromPlist:(NSString *)animPlist delay;(float)delay;

    With this:
    Code: Select all

    -(CCAnimation *)animationFromPlist:(NSString*)animPlist withKey:(NSString*)animKey delay:(float)delay {
       
        NSString*plistPath =[[NSBundle mainBundle] pathForResource:animPlist ofType:@"plist"];
        NSDictionary *plistDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
        NSArray*animImages = [plistDict objectForKey:animKey];
        NSMutableArray*animFrames =[NSMutableArray array];
        for(NSString*animImage in animImages){
            [animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:animImage]];
        }
       
        return[CCAnimation animationWithFrames:animFrames delay:delay];
    }


    Then replace these two lines of code in your - (id)init; method:

    Code: Select all
    laughAnim =[self animationFromPlist:@"laughAnim" delay:0.1];       
    hitAnim =[self animationFromPlist:@"hitAnim" delay:0.02];


    To use the new animationFromPlist:withKey:delay: method!
    It should look like this:

    Code: Select all

    // Reference Sprite Animations
            laughAnim =[self animationFromPlist:@"moleAnim" withKey:@"laugh" delay:0.1];       
            hitAnim =[self animationFromPlist:@"moleAnim" withKey:@"hit" delay:0.02];
            [[CCAnimationCache sharedAnimationCache] addAnimation:laughAnim name:@"laughAnim"];
            [[CCAnimationCache sharedAnimationCache] addAnimation:hitAnim name:@"hitAnim"];


    And that's it! No more plist problems! Hope this helps some people.
    joeapps
  • Hey All, its been a while since I posted, my project is nearly done :D (claps excitedly) :D .
    But on testing I found a strange bug, I wonder if someone could hopefully shed some light on.

    Im using code from your tutorial , I have this in every scene as initially there was alot of text in all scenes but thats slowly been replaced with images.

    Code: Select all
    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];


    - (float)convertFontSize:(float)fontSize {
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
            return fontSize * 2;
        } else {
            return fontSize;
        }
    }


    So that the font scales when its either iphone or retina(I Havent made my game for ipad,I dont think it would be as effective on there).
    And what I have noticed I start my game all is well text is correct size, go into either Options menu,tutorialmenu or start game, then if you GO BACK to the menu the text from then on will have shrunk...
    I didnt notice it before, so either Its something I just did or I just never noticed.
    Its not a biggie, Its just annoying... any ideas?
    Thomas
  • Hi Ray

    Thanks again for your awesome page and tutorials, i have followed the tut, changing things to make it run in Xcode 4 and everything is fine till the end of the tut. After all the tappable thing i run the code and have this error in setTappable:

    - (void)setTappable:(id)sender {
    CCSprite *mole = (CCSprite *)sender;
    [mole setUserData:TRUE]; <<<Incompatible integer to pointer conversion sending 'int' to parameter of type 'void *';
    }

    I don't understand why your code has this warning but does not crash and mine does... any idea?

    Thanks in advance

    Jose
    jreycas
  • Ahhhh, found it, the code in popMole to call the tappable functions... i had CCCallFunc instead of CCCallFuncN... it was too late and was tired, i have checked the code and it was like one hundred lights pointing at it...

    well, everything it's okay :P
    jreycas
  • @rwenderlich hey do you think you will ever expand on this tutorial? I'm useing this tutorial to make a multi level wack a mole game and i want to know how to make stronger moles that pop up at different times. when i tried i never made them harder (yet) but i put in different images for the "harder" ones and they would sometimes come up at the same time as the others just overlaping.

    thanks in advance to anyone who coud help out
    AustinF
  • I finally finish the conversion of this game from cocos2d-iphone to cocos2d-x,

    The cocos2d-family framework is very powerfull.
    fabiobh
  • Hey.I just started with the tutorials.I am still in the part where the laugh animation is loaded. When I hit the run button,the first mole pops out and then I recieve an 'EXC_BAD_ACCESS' error.I've checked my code again and again for any error but I don't find one.Can anyone help please?
    manish.spangle
[ 1 , 2 , 3 , 4 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

  • Charlie Fulton

... 49 total!

Update Team

  • Andy Pereira
  • Riccardo D'Antoni

Editorial Team

  • Matt Galloway

... 23 total!

Code Team

  • Orta Therox

... 3 total!

Translation Team

  • Jesus Guerra
  • Victor Grushevskiy

... 33 total!

Subject Matter Experts

... 4 total!