Introduction to CocosBuilder

Ali Hafizji
Learn how to use CocosBuilder - an Interface-Builder like tool for Cocos2D!

Learn how to use CocosBuilder – an Interface-Builder like tool for Cocos2D!

This is a blog post by iOS Tutorial Team member Ali Hafizji, an iOS and Android developer working at Tavisca Solutions.

CocosBuilder is a free Interface Builder-like tool for Cocos2D that allows you to quickly and easily layout the sprites, layers, and scenes for your game.

CocosBuilder is ideal for quickly laying out menus and other game interface scenes, where it would be a pain to have to place all of the elements manually.

Before CocosBuilder, creating even a basic interface for a game in Cocos2D was a pain in the butt. When adding a menu or button into your game, you’d usually do something like this:

  • Make a guess. “Uhh, yeah I think the button should be at about 50×50.”
  • Build and run. “Oops, that’s not quite right!”
  • Tweak the guess. “I think 60×50 would be better!”
  • Rinse and repeat. “Doh, still not right. Gahh!”

CocosBuilder eliminates all of these steps, including the multiple iterations of setting and testing sprite positions. This frees you to focus on the game logic rather than pulling your hair out over the interface, making you more productive as a developer – which is always a good thing, right? :]

This beginning-level tutorial will show you how to use CocosBuilder to create simple interfaces. You’ll learn how to set up menus, buttons, particle systems, basic level layouts, and connect the interfaces you make to code!

You’ll recreate the Cat Jump game originally developed for Ray and Rod’s Cocos2D via Minigames workshop. Of course, this time around, there will be a lot less coding. :] You’ll see how that CocosBuilder will save you a ton of time and code!

This tutorial assumes you have some basic familiarity with Cocos2D. If you are new to Cocos2D, check out some of the other Cocos2D tutorials on this site first.

Let’s build some interfaces, the easy way!

Introducing Cat Jump!

Cat Jump is a game about a cat having a bad day. He’s just minding his own business, walking along the street, when all of a sudden everyone’s out to run him over – cars, trucks, even kids on tricycles!

Here is a preview of the game in action:

Your poor cat only has a limited number of lives (9 of course!), and your goal is to stay alive as long as possible by dodging the obstacles.

To get a preview of the game you’ll be making, download this version of CatJump made without CocosBuilder. Open it in Xcode, build and run, and play the game for a while – it’s a lot of fun!

After playing the game a bit, peek through the code – you’ll notice a ton of hardcoded offsets to set up positions for menus, labels, and sprites. Those were a pain to set up – you’ll learn a better way!

In this tutorial, you will recreate Cat Jump by performing the following tasks:

  • Remake the Main Menu scene in CocosBuilder. Right now the Main Menu layout is done by placing items with hardcoded offsets – you’ll delete all that code and use CocosBuilder instead!
  • Add an Options scene. You’ll add a new Options scene with three buttons for difficulty level: Easy, Medium, and Hard. Tapping on any option will invoke a selector in your code.
  • Add an About scene. You’ll also add an About page with a small particle system (in other words, a cool special effect). Using CocosBuilder, you will not write a single line of code to do this!
  • Rebuild the Game Scene using CocosBuilder. You’ll remove manual sprite placement and use CocosBuilder to help you out instead!
  • Rebuild the Game Over Scene using CocosBuilder. Again, no more hardcoded offsets! :]

Finally, you’ll learn some handy troubleshooting tips for how to prevent and resolve common problems with CocosBuilder. Not that CocosBuilder is error-prone, but there are always a few obstacles to dodge, kinda like kids on tricycles! You’ll put them on your radar now, saving you time later.

Getting Started With CocosBuilder

If you haven’t already, download CocosBuilder by visiting the CocosBuilder website and selecting the Downloads tab. Make sure to get the latest version, which at the time of writing is 2.1. While you’re at it, download the Examples file as well.

Extract the CocosBuilder application from the archive and copy it to the Applications folder (as usual for an app installation).

Before you create a new project in CocosBuilder, you need to create a directory for your project. This is the place where you will store all the game resources/assets.

Note: You can also create a project and reference resources wherever they may currently reside on your hard drive, but find creating a directory structure like this helps me know where all my files are and keep things organized.

Create a new directory on the Desktop and name it CocosBuilderFiles. Then create two subfolders within this directory and name them Resources and Scenes.

The Resources folder will, obviously, contain all the resources for the game (spritesheets, fonts, etc.). If you want, you can copy all of the resources from the Cat Jump project you downloaded earlier, but to make things simpler I have created a ZIP file with everything you need. Download it, extract it and copy its contents to the Resources folder.

Fire up CocosBuilder and select File\New\New Project from the menu. Name the project CatJump and save it in the CocosBuilderFiles directory.

Now that you’ve created the project, you’ll see the Resources and Scenes folders in the Project sidebar on the left in CocosBuilder. Also notice that CocosBuilder automatically created a new folder called ccbResources. Along with that, you will see a new file called HelloCocosBuilder.ccb. Double-click the file to see its contents.

It’s a very basic layout with a label that says “Hello CocosBuilder”:

Don’t worry, your Cat Jump interface will be a little more complicated than that. :]

Look Ma, No Code!

You’ll start off the re-imagined Cat Jump by creating the game’s Main Menu. This scene will have three buttons:

  • Play – This will launch the game!
  • Options – This will show an Options scene where users can select the game’s difficulty level.
  • About – This will show an About scene that instructs users how to play the game.

The first thing you need to do is delete the HelloCocosBuilder.ccb file, since that’s just a default file created by CocosBuilder.

Note: You would think it should be fairly straightforward to delete an unused scene file from a CocosBuilder project, but I wasn’t able to do this directly. I had to resort to closing CocosBuilder, deleting HelloCocosBuilder.ccb via Finder, and then reopening the project in CocosBuilder. If anyone has knows an easier way to do this, please comment in the forums!

Next, go to File\New\New File. This will open a dropdown menu. Make sure the Root object type is CCLayer, and select iPhone Landscape and iPhone 5 Landscape from the options given.

When you’re done, press Create, name the file MainMenuScene and save it in your Scenes folder.

The project pane should now look something like this:

And with that, you’ve created your first scene! Now how about adding some sprites?

Click the CCSprite button on the toolbar. Hint: It’s the one circled in the image below. :]

This will add a new sprite to the scene.

Select the new sprite and set its frame to Title_catjump.png. You can use the right sidebar, which has the properties of the currently selected item, to do this. Use the Sprite frame property in the CCSprite section, and navigate the dropdown to select Title_catjump.png under Resources\Normal.

Next, center the sprite by simply dragging it to the center position. Or, if you’d rather be exact, you can set the Anchor Point (under the CCNode section) on the right sidebar to 0 for both X and Y values.

Note that this will only work if you have the Position set to be the bottom-left corner. If you change the position, then you’ll have to set the X and Y values accordingly. Have some fun – see what happens when you try different values. :]

Great! You now have your background image all set. The next thing to do is add buttons for the menu items.

Tap the CCControlButton toolbar item to create a new button on the screen.

The new button comes with a nice background image, which you can find in the ccbResources folder that CocosBuilder created. Set the title of this button to Play via the CCControlButton section on the right sidebar, which includes a Title property.

Adjust the position of the button. You can place it anywhere you like by dragging it, or set an absolute position using the right sidebar.

OK then, more buttons! Repeat the above process to add two more. Set the title for the first one to Options and the second to About. Your final layout should be similar to the one shown in the image below:

Hurray, the layout for the first screen is now complete!

Hooking up a Layer to a Class

Before you proceed, you need to make a few adjustments. Whenever you set up a scene using a layer created in CocosBuilder, if you want the scene’s layer to be a custom class, you need to tell CocosBuilder the name of that class.

For example, if you initialize a scene using the MainMenuScene file and you want its layer to be a class that you create, then you need to specify the name of that class in the Code Connections section of CocosBuilder.

Select the MainMenuScene.ccb file and select the CCLayer root node from the timeline.

In the Code Connections section on the right, set the Custom class textbox to MainMenuLayer. Now when you initialize this scene, CocosBuilder will look for a class named MainMenuLayer and use it to instantiate the scene’s layer.

Next you need to publish the CocosBuilder interface file. To do this, simply go to the File menu and select Publish. This will save a new file named MainMenuScene.ccbi to the Scenes directory.

Enough with CocosBuilder for now – it’s time to try this out in a Cocos2D project!

Time for Code!

First, make sure you have the latest version of Cocos2D 2.X installed – 2.1-beta4 at the time of writing this tutorial.

Then start up Xcode and create a new project with the iOS\cocos2d v2.x\cocos2d iOS template. Enter CatJump as the Product Name, enter the Company Identifier you used when creating your App ID, and set Device Family to iPhone:

Finish creating the project and save it somewhere on your hard drive.

Next, create a new group under the project root called Scenes, and drag and drop the CCBI file from the scenes directory into this group. Make sure the “Copy items to destination group’s folder (if needed)” is checked, and that the CatJump target is selected under “Add to targets.”

You now need to add CCBReader to your project. CCBReader is bundled with the example files you downloaded earlier from the CocosBuilder website. Extract the example files archive (if you hadn’t already) to a location on your hard drive. Find the CCBReader folder under Add to Your Project\cocos2d-iphone.

Drag the complete CCBReader folder to your project. Make sure that “Create groups for any added folders” is selected and that “Copy items into destination group’s folder” is checked. Do the same with the CCControlExtension folder.

Next, create a new group under CatJump and name it Layers. Create a new file with the iOS\cocos2d v2.x\CCNode class template under this group. Make it a subclass of CCLayer and name it MainMenuLayer.m.

Before you write any code, open AppDelegate.m and add the following import statement (at the top below the existing #import statements):

#import "CCBReader.h"

Next, open application: didFinishLaunchingWithOptions: and find this line:

[director runWithScene: [IntroLayer scene]];

Once you find that line, replace it with the following:

[director runWithScene: [CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]];

And that’s all the code you need in order to run a scene created with CocosBuilder! The CCBReader class will parse the MainMenuScene.ccbi file and create the scene for you!

But before you can run the application, there’s one final step. :] Remember the background image you added to your scene and the button images from the ccbResources folder in your CocosBuilder project folder?

Those images aren’t in your project yet, and you need them in order for the app to function properly. Otherwise, the app will crash. (In fact, you can test this right now by trying to run the app…)

From your CocosBuilder project folder, select all the files in the Resources folder and drag-and-drop them into the Resources folder in your Xcode project. Do the same with all the files in the ccbResources folder. As before, ensure that the “Copy items into destination group’s folder” is checked, that “Create groups for any added folders” is selected, and that the CatJump target is selected.

Xcode

Now, build the app. If you get an error while compiling CCBReader.m, replace the line with the error with the following:

return [_bundle pathForResource:resource ofType:ext inDirectory:subpath];

Then run the app. When the app starts, you should see the Main Menu with the three buttons, as shown below:

The Main Event

Congrats, you now have a working layout made with CocosBuilder, with only one line of code! :]

But how on earth are you going to get events when the user taps on any one of those buttons?

Actually, CocosBuilder makes this task very easy! It allows you to specify the name of the method to invoke when the user taps a button. You can also specify the event for which the method will be invoked (via a checkbox).

Let’s add this functionality to MainMenuScene. Open MainMenuScene.ccb using CocosBuilder and select the Play button. Set its Tag property to 1 via the CCNode subsection in the right side pane.

Next, go to the CCControl subsection and fill the Selector textbox with the name of the method that will be invoked, buttonPressed:. Also set the Target as Document root (again, to hook it up to the layer).

Do the same for the other two buttons, but with different tags – set the Tag property of the Options button to 2, and the About button to 3.

Awesome! You have wired your buttons to call a selector present in the CCLayer. Save your changes, publish MainMenuScene.ccb again, and copy the published file to the Xcode project folder.

Note: You will not be able to drag-and-drop the file onto the Xcode project as before, since the file already exists in the project. So either delete the file from the project first, or drag-and-drop the new file via Finder.

Next open MainMenuLayer.m in Xcode and add the following import statements:

#import "CCControlButton.h"
#import "CCBReader.h"

Also add the following #defines for a few constants right below the #import statements. These refer to the tags for each of the three buttons you placed on the scene:

#define PLAY_BUTTON_TAG 1
#define OPTIONS_BUTTON_TAG 2
#define ABOUT_BUTTON_TAG 3

Now what about that buttonPressed: method? Add it to MainMenuLayer.m:

-(void)buttonPressed:(id)sender {
    CCControlButton *button = (CCControlButton*) sender;
    switch (button.tag) {
        case PLAY_BUTTON_TAG:
            [[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"GameScene.ccbi"]]];
            break;
        case OPTIONS_BUTTON_TAG:
            [[CCDirector sharedDirector] replaceScene:[CCTransitionFlipAngular transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"OptionsScene.ccbi"]]];
            break;
        case ABOUT_BUTTON_TAG:
            [[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"AboutScene.ccbi"]]];
            break;
    }
}

This method is self-explanatory: all it does is handle each button tap separately. For example, when the About button is tapped, the About Scene is displayed.

Build and run the game. You should now have a completely functional Main Menu scene, like the one shown below:

Awesome, you have just created your first scene and you hardly wrote any code for it. :]

Of course, you might have noticed that the code for buttonPressed: refers to several CCBI files that you haven’t created yet. Hence, tapping on any of the buttons on the Main Menu crashes the game, since those scenes are not in place yet.

That’s what you’re going to do next – fill in the gaps!

Difficult Is Not An Option!

Like the Main Menu, the Options scene will have three buttons, and creating this scene will be just as painless.

In the case of the Options scene, the buttons will allow the user to select a difficulty level of Easy, Medium or Hard. There will also be a back button to return to the Main Menu.

Open CocosBuilder and create the new scene by selecting File\New\New File (follow the same steps as when you created the MainMenuScene), name it OptionsScene and save it in the Scenes directory.

Add three buttons to the scene and set their titles to Easy, Medium, and Hard. Then set their tags as 1, 2, and 3, respectively.

To get events when the user taps these buttons, you need to register a method to be called. Just as you did with MainMenuScene, set the selector for each of the buttons to difficultyButtonPressed: and the Target to Document Root.

Note: Wondering what Document Root means? It means the root node in the “Default Timeline” tree. Soon you will set the root node (CCLayer) to a custom class – OptionsLayer. That means that OptionsLayer will be the Document Root.

The layout should look something like this:

Now for that back button. This time, instead of adding a CCControlButton, add a CCMenu with the back button as a menu item.

Press the CCMenu button on the tool bar.

This will create a CCMenu node and add it to the OptionsScene layer. Now, add a CCMenuItemImage by pressing the CCMenuItemImage button on the tool bar.

Set the Normal and Selected sprites for this CCMenuItem as btn-back-0.png and btn-back-1.png. These properties can be set from the CCMenuItemImage subsection on the right pane.

Place the back button in the top-left corner of the scene and set the selector as backButtonPressed:. Don’t forget to set the Target as Document root.

That’s it! The scene should now look like this:

Just as you did with the MainMenuScene, add a custom class for the OptionsScene. Name it OptionsLayer.

As before, save your changes, publish the scene, and add the CCBI file to the Xcode project.

Switch to Xcode, create a new class under the Layers group and name it OptionsLayer (make sure it extends CCLayer) – just as you did before.

Next, add the following import and declare statements to the top of OptionsLayer.m:

#import "CCBReader.h"
#import "CCControlButton.h"
 
#define DIFFICULTY_EASY_BUTTON_TAG 1
#define DIFFICULTY_MEDIUM_BUTTON_TAG 2
#define DIFFICULTY_HARD_BUTTON_TAG 3

Also add the following methods:

-(void)backButtonPressed:(id)sender {
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFlipAngular transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]]];
}
 
-(void)difficultyButtonPressed:(id)sender {
    CCControlButton *button = (CCControlButton*) sender;
    NSString *difficultyLevel = @"Hard";
    if (button.tag == DIFFICULTY_EASY_BUTTON_TAG) {
        difficultyLevel = @"Easy";
    } else if(button.tag == DIFFICULTY_MEDIUM_BUTTON_TAG) {
        difficultyLevel = @"Medium";
    }
    NSLog(@"Difficulty is set to %@", difficultyLevel);
}

All of this should be familiar to you by now. :] backButtonPressed: will take the user back to the Main Menu scene.

difficultyButtonPressed: in its current form doesn’t actually set a difficulty level, but it will log the user’s selection. Feel free to implement the difficulty level for the game later, on your own – will you accept the challenge? :]

Build and run the game, and now you have two fully functional scenes. You’re halfway to a full game interface!

It’s About Time For… Fire!

About scenes exist to give users additional information about your game or app – how to play or use the app, who made it and what version number it is, and so on.

Your About scene will be special: it will have a burning inferno! Not only will it look cool, but it will serve the extra purpose of teaching you how to add a particle system (i.e. special effect) using CocosBuilder. :]

Switch to CocosBuilder, create a new file named AboutScene and save it in the Scenes directory.

To begin the layout, press the CCParticleSystemQuad button on the tool bar.

This will create a fire particle system. Select the particle system and change the Particle texture property to cat_leap_1.png. Play around with the CCParticleSystemQuad properties until you’re happy with what you have. Then place the particle system as shown below:

Now you need some text. Add a CCLabelBMFont by pressing the following button on the tool bar:

Set the Font file property of the label to Arial.fnt (present in the Resources directory). Then add two more labels, one below the other, and give them the same font.

Break up the following text between the three labels: Help the cat jump over all the obstacles trying to run him over. You’re not going for anything nuanced here!

At this stage, your About scene should look something like this:

You’re almost done with this scene! All that’s left is the back button – make sure the user can get back home!

Add a back button in the top-left corner, just as you did earlier. Set its selector as backButtonPressed: and the Target as Document root.

The final step is to add a custom class for this scene’s layer, just like for the previous two scenes. Name the custom class AboutLayer, as shown below:

Following the now-familiar routine, save your changes, publish the scene, and add it to the Xcode project. Then switch to Xcode and create a new Cocos2D class under the Layers group. Name it AboutLayer and make sure it extends CCLayer.

Now open AboutLayer.m and add the following import statement:

#import "CCBReader.h"

Also add the following method:

-(void)backButtonPressed:(id)sender {
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFlipAngular transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]]];
}

This method will be invoked when the user presses the back button and it replaces the current scene with the MainMenu scene.

Build and run the game again. You should see all the functionality you expect: tapping on the About button displays the AboutScene, and the back button returns you to the Main Menu. Not bad for a few minutes of your time, eh?

Game On!

Finally, time to the introduce the star of the game – the cat having a rough day! All you need to do here is place all the sprites that the game requires into the correct spots, and you’ll do the rest via code. So, let’s get started!

Switch to CocosBuilder and create a new file. Name it GameScene and save it in the Scenes directory.

Next, press the CCSprite button on the toolbar to create a new sprite. Set the frame of this sprite as bg.png and set the position as the center of the screen. Your scene should resemble the one shown below:

Now you need to add the game’s main character – the cat! Add another sprite to the layer by pressing the CCSprite button again. Set the frame of this sprite to cat_stand_1.png and give it a fixed position of X:75 and Y:75.

You also need to add two CCLabelBMFonts to the layer. These will show the number of lives the cat has left and the number of dodges made.

Press the CCLabelBMFont button twice and set the text of the labels to Dodges: and Lives:, respectively. Also set the font for each to Arial.fnt. Place the labels in the top left and right corners of the screen, as shown below:

Excellent! You’re all set.

Or are you? While you’ve placed all the sprites on the layer, you don’t have a way to reference them in code. For example, how would you refer to the cat in the game logic? Don’t get your tail in a knot – CocosBuilder gives you an easy way.

Let’s start with your hero, the cat. Select the cat sprite and in the right pane, under Code Connections, you will see a dropdown below the Custom class label.

Select Doc root var from the dropdown and set the text box next to it to cat. This will associate a variable named “cat” with a reference to this sprite. “Doc root var” specifies that this variable will be present in the document root, in this case the layer class.

That’s all there is to it! Now repeat the same for the labels, and name their reference variables livesLabel and dodgesLabel.

You can guess what comes next, right? Just like the previous scenes, this scene also requires a custom layer. So set the custom class property for the root layer of this scene to GameLayer:

Save your changes, publish the scene, and add the CCBI file to the project. Then switch to Xcode and create a new class in the Layers group, naming it GameLayer and making sure it extends CCLayer.

Now would be the time in your game development process to write the game logic. But since the focus of this tutorial is CocosBuilder (not game logic), simply replace GameLayer.m with this long code block:

#import "GameLayer.h"
#import "CCBReader.h"
#import "SimpleAudioEngine.h"
 
#define kVehicleTypeNone -1
#define kVehicleTypeRedCar 0
#define kVehicleTypeYellowCar 1
#define kVehicleTypeDog 2
#define kVehicleTypeKid 3
 
@interface GameLayer() {
    CCLabelBMFont *livesLabel;
    CCLabelBMFont *dodgesLabel;
    CCSprite *cat;
 
    CCNode *_vehicles;
    BOOL _invincible;
    BOOL _jumping;
    double _nextSpawn;
    int _lives;
    int _dodges;
    CCSpriteBatchNode *_catJumpBatchNode;
    CCAnimation *_catJumpAnimation;
}
@end
 
@implementation GameLayer
 
- (id) init {
    self = [super init];
    if (self) {
 
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"CatJumpAtlas.plist"];
 
        _catJumpBatchNode = [CCSpriteBatchNode batchNodeWithFile:@"CatJumpAtlas.png"];
        [self addChild:_catJumpBatchNode z:1];
 
        _catJumpAnimation = [CCAnimation animation];
        [_catJumpAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"cat_leap_1.png"]];
        [_catJumpAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"cat_leap_2.png"]];
        [_catJumpAnimation setDelayPerUnit:0.625f];
        [_catJumpAnimation retain];
 
        // If you want to add this to the AnimationCache instead of retaining
        //[[CCAnimationCache sharedAnimationCache] addAnimation:catJumpAnimation name:@"catJumpAnim"];
 
        // Dog Animation
        CCAnimation *dogAnimation = [CCAnimation animation];
        [dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_1.png"]];
        [dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_2.png"]];
        [dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_3.png"]];
        [dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_4.png"]];
        [[CCAnimationCache sharedAnimationCache] addAnimation:dogAnimation name:@"dogAnimation"];
 
        // Kid Animation
        CCAnimation *kidAnimation = [CCAnimation animation];
        [kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_1.png"]];
        [kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_2.png"]];
        [kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_3.png"]];
        [kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_4.png"]];
        [[CCAnimationCache sharedAnimationCache] addAnimation:kidAnimation name:@"kidAnimation"];
 
 
        self.isTouchEnabled = YES;
 
        [self scheduleUpdate];
 
        _vehicles = [CCNode node];
        [self addChild:_vehicles];
 
        _lives = 9;
        _dodges = 0;
 
        double curTime = [[NSDate date] timeIntervalSince1970];
        _nextSpawn = curTime + 4;
 
    }
    return self;
}
 
- (void) didLoadFromCCB { 
    [self setLives:_lives];
    [self setDodges:_dodges];
}
 
- (void) setDodges:(int) noOfDodges {
    dodgesLabel.string = [NSString stringWithFormat:@"Dodges:%d", noOfDodges];
}
 
- (void) setLives:(int) noOfLives {
    livesLabel.string = [NSString stringWithFormat:@"Lives:%d", noOfLives];
}
 
- (void)carDone:(id)sender {
 
    CCSprite *vehicle = (CCSprite *)sender;
    [vehicle removeFromParentAndCleanup:YES];
 
    _dodges++;
    [self setDodges:_dodges];
}
 
- (void)doneInvincible {
    _invincible = FALSE;
}
 
- (void)update:(ccTime)dt {
    CGSize winSize = [CCDirector sharedDirector].winSize;
    CCSprite *vehicleSprite;
    // Spawn Vehicles (new)
    double curTime = [[NSDate date] timeIntervalSince1970];
    if (curTime > _nextSpawn) {
 
        int randomVehicle = arc4random() % 4;
 
        if (randomVehicle == kVehicleTypeRedCar) {
            // Red Car
            vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car1_1.png"]];
            [vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeRedCar]];
            CCSprite *wheel1 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car1_tire.png"]];
            CCSprite *wheel2 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car1_tire.png"]];
            id tireRotateAction1 = [CCRotateBy actionWithDuration:1.0f angle:360.0f]; // Ray, these are backwards on purpose as a lab exercise.
            id tireRotateAction2 = [CCRotateBy actionWithDuration:1.0f angle:360.0f];
            [wheel1 runAction:[CCRepeatForever actionWithAction:tireRotateAction1]];
            [wheel2 runAction:[CCRepeatForever actionWithAction:tireRotateAction2]];
            [vehicleSprite addChild:wheel1];
            [vehicleSprite addChild:wheel2];
            [wheel1 setPosition:ccp(65,18)];
            [wheel2 setPosition:ccp(212,18)];
 
        } else if (randomVehicle == kVehicleTypeYellowCar) {
            // Yellow Car (Same code as Red Car except for wheel placement, re-listed for clarity. Consilidate in your own games)
            vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car2_1.png"]];
            [vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeYellowCar]];
            CCSprite *wheel1 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car2_tire.png"]];
            CCSprite *wheel2 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car2_tire.png"]];
            id tireRotateAction1 = [CCRotateBy actionWithDuration:1.0f angle:-360.0f];
            id tireRotateAction2 = [CCRotateBy actionWithDuration:1.0f angle:-360.0f];
            [wheel1 runAction:[CCRepeatForever actionWithAction:tireRotateAction1]];
            [wheel2 runAction:[CCRepeatForever actionWithAction:tireRotateAction2]];
            [vehicleSprite addChild:wheel1];
            [vehicleSprite addChild:wheel2];
            [wheel1 setPosition:ccp(62,15)];
            [wheel2 setPosition:ccp(195,15)];
 
        } else if (randomVehicle == kVehicleTypeDog) {
            // Dog
            vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_1.png"]];
            [vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeDog]];
 
            // In your code, check that the animationByName did not return nil (due to memory warnings)
            CCAnimation *vehicleAnimation = [[CCAnimationCache sharedAnimationCache] animationByName:@"dogAnimation"];
 
            vehicleAnimation.restoreOriginalFrame = NO;
            vehicleAnimation.delayPerUnit = 0.5f/ vehicleAnimation.frames.count;
            id animationAction = [CCAnimate actionWithAnimation:vehicleAnimation];
 
            [vehicleSprite runAction:[CCRepeatForever actionWithAction:animationAction]];
 
        } else {
            // Kid on Bike (Same code as Dog, re-listed for clarity. Consilidate in your own games)
            vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_1.png"]];
            [vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeKid]];
 
            // In your code, check that the animationByName did not return nil (due to memory warnings)
            CCAnimation *vehicleAnimation = [[CCAnimationCache sharedAnimationCache] animationByName:@"kidAnimation"];
 
            vehicleAnimation.restoreOriginalFrame = NO;
            vehicleAnimation.delayPerUnit = 0.5f/ vehicleAnimation.frames.count;
            id animationAction = [CCAnimate actionWithAnimation:vehicleAnimation];
 
            [vehicleSprite runAction:[CCRepeatForever actionWithAction:animationAction]];
 
        }
 
        // Common placement and movement code for all vehicles
 
        vehicleSprite.position = ccp(winSize.width + vehicleSprite.contentSize.width/2, 75);
        [_catJumpBatchNode addChild:vehicleSprite];
 
        [vehicleSprite runAction:[CCSequence actions:
                                  [CCMoveBy actionWithDuration:1.25 position:ccp(-winSize.width-vehicleSprite.contentSize.width, 0)],
                                  [CCCallFuncN actionWithTarget:self selector:@selector(carDone:)],
                                  nil]];
 
        float randomInterval = arc4random() % 3 + 1.5;
        _nextSpawn = curTime + randomInterval;
 
 
    }
 
 
    // Check for collisions
    if (!_invincible) {
        float insetAmtX = 10;
        float insetAmtY = 10;
        BOOL isCatColliding;
        CGRect catRect = CGRectInset(cat.boundingBox, insetAmtX, insetAmtY);
        CGRect vehicleRect;
        for (CCSprite *vehicle in _catJumpBatchNode.children) {
            if ([vehicle tag] == 1) {
                continue;  // No need to check if the Cat collides with itself
            }
            isCatColliding = NO;
            NSNumber *vehicleTypeNumber = (NSNumber*)[vehicle userData];
            int vehicleType = [vehicleTypeNumber intValue];
 
            if (vehicleType == kVehicleTypeRedCar) {
                CGPoint boundingBoxOrigin = vehicle.boundingBox.origin;
                CGRect carHood = CGRectMake(boundingBoxOrigin.x+10,boundingBoxOrigin.y , 40,80);
                insetAmtX = 50;
                insetAmtY = 10;
                vehicleRect = CGRectInset(vehicle.boundingBox,insetAmtX,insetAmtY);
 
                if ((CGRectIntersectsRect(catRect,carHood)) ||
                    (CGRectIntersectsRect(catRect, vehicleRect))) {
                    isCatColliding = YES;
                    CCLOG(@"Collided with Red Car");
                }
 
            } else if (vehicleType == kVehicleTypeYellowCar) {
                CGPoint boundingBoxOrigin = vehicle.boundingBox.origin;
                CGRect carHood = CGRectMake(boundingBoxOrigin.x+10,boundingBoxOrigin.y , 68,65);
                insetAmtX = 68;
                insetAmtY = 10;
                vehicleRect = CGRectInset(vehicle.boundingBox,insetAmtX,insetAmtY);
 
                if ((CGRectIntersectsRect(catRect,carHood)) ||
                    (CGRectIntersectsRect(catRect, vehicleRect))) {
                    isCatColliding = YES;
                    CCLOG(@"Collided with Yellow Car");
                } 
 
 
            } else {
                // Dog or Kid
                CGRect vehicleRect = CGRectInset(vehicle.boundingBox, insetAmtX, insetAmtY);
                if (CGRectIntersectsRect(catRect, vehicleRect)) {
                    isCatColliding = YES;
                }
            }
 
 
            if (isCatColliding == YES) {
                // Play sound, take a hit, invincible, break out of the loop
                [[SimpleAudioEngine sharedEngine] playEffect:@"squish.wav"];            
                _invincible = TRUE;
                [cat runAction:[CCSequence actions:
                                [CCBlink actionWithDuration:1.0 blinks:6],
                                [CCCallFunc actionWithTarget:self selector:@selector(doneInvincible)],
                                nil]];     
                _lives--;
                [self setLives:_lives];
 
                if (_lives <= 0) {
                    [[CCDirector sharedDirector] replaceScene:[CCTransitionJumpZoom transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"GameOver.ccbi"]]];
                }
                break;
            }
        }
 
    }
}
 
- (void)doneJump {
    _jumping = FALSE;
}
 
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 
    if (!_jumping) {
 
        _jumping = TRUE;
        [[SimpleAudioEngine sharedEngine] playEffect:@"meow.wav"];
 
        CCLOG(@"Making the Cat Jump");
        _catJumpAnimation.restoreOriginalFrame = YES;
        CCAnimate *jumpAnimation = [CCAnimate actionWithAnimation:_catJumpAnimation];
 
        CCJumpBy *jumpAction = [CCJumpBy actionWithDuration:1.25 position:ccp(0,0) height:200 jumps:1];
        CCCallFunc *doneJumpAction = [CCCallFunc actionWithTarget:self selector:@selector(doneJump)];
        CCSequence *sequenceAction = [CCSequence actions:jumpAction,doneJumpAction, nil];
 
 
		[cat runAction:[CCSpawn actions:jumpAnimation,sequenceAction, nil]];
    }
 
}
@end

And that’s your work done for you! :] Feel free to look through the code to get an idea of what’s going on with the game logic, but don’t worry about it too much – again our focus is CocosBuilder here.

Pay attention to the following variables set up at the top, right after the #defines:

    CCLabelBMFont *livesLabel;
    CCLabelBMFont *dodgesLabel;
    CCSprite *cat;

These variables, as I’m sure you remember, correspond to the variables you set up in CocosBuilder. They will be automatically initialized to the cat sprite, lives label, and dodges label.

Build and run the game. Now when you press the Play button, you can actually play the game.

HOORAY! You’ve successfully implemented the gameplay scene. That was a lot easier that dodging kids on tricycles.

It’s Not Over Until It’s Game Over

Did you think you were done? Not quite. :]

If you play the game until the cat loses all its lives, you’ll notice that the game crashes. That’s because there’s a Game Over scene that CCBReader is supposed to load that it can’t find. That might be because you didn’t create it yet. :p

This scene will have two buttons:

  • Main Menu: This will return the user to the Main Menu.
  • Replay: This will allow the user to play the game again.

Switch to CocosBuilder and create a new file named GameOver. Add the above two buttons as CCControlButtons. Make sure to set their tags to 1 and 2. Also set their selector as buttonPressed: and the Target to Document root.

Finally, set the Custom class for the root layer to GameOverLayer, save your changes, and publish the file.

Switch back to Xcode and add the CCBI file to the project. Then, create a new Cocos2D class under the Layers group and name it GameOverLayer, making sure it extends the CCLayer class.

Add the following import statements to GameOverLayer.m:

#import "CCControlButton.h"
#import "CCBReader.h"

Also add these define statements:

#define MAIN_MENU_BUTTON_TAG 1
#define PLAY_AGAIN_BUTTON_TAG 2

Now add the button tap handler:

-(void)buttonPressed:(id)sender {
    CCControlButton *button = (CCControlButton*) sender;
    switch (button.tag) {
        case MAIN_MENU_BUTTON_TAG:
            [[CCDirector sharedDirector] replaceScene:[CCTransitionFlipY transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]]];
            break;
 
        case PLAY_AGAIN_BUTTON_TAG:
            [[CCDirector sharedDirector] replaceScene:[CCTransitionFadeUp transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"GameScene.ccbi"]]];
            break;
    }
}

Build and run the game. At this point, you should have a fully functional game.

Now wasn’t that so much easier than guesswork and hard-coding for each of the scenes? :]

Troubleshooting CocosBuilder

CocosBuilder is a great tool for setting up scenes quickly and efficiently. However, it isn’t very good at communicating when something is wrong with a scene. So, in order to save you some frustration (and of course, the pulling out of your hair in handfuls), I’ve put together a checklist to go through, in case the scene doesn’t work properly or you run in to unexpected crashes.

  • Check that you saved your CocosBuilder changes before publishing. This is important. CocosBuilder does not appear to warn you if you have unsaved changes when you hit Publish. So make it a habit to always save your changes before you publish a scene.
  • If you drag and drop your CCBI files to your Xcode project, always ensure that your target is selected. Normally, once you check the proper target via the “Add to targets” setting, it seems to stick. But not for CCBI files. So, always verify that the CCBI files are added to the target, especially if you have a crash when a scene is supposed to be loaded.
  • Check the console output for helpful messages. While CocosBuilder might not tell you what went wrong, the console output might show a message that helps you figure out what went wrong. If it says “File not found: GameOver.ccbi”, then that means that the GameOver.ccbi file has either not been added to the project, is not part of the current build target, or there’s a mistake in the file name.
  • Verify that you’ve spelled things correctly. When you set up things like variables, custom classes, and selectors for events, what you specify in the CocosBuilder file has to match what’s in your CCLayer subclass.

If you watch out for the above as you work, you’ll save yourself headaches and, when a problem does arise, you’ll hopefully be able to pounce on it like a frisky cat on a mouse.

Where To Go From Here?

Here is all of the source code for the final project.

You should now be ready to create games on your own using CocosBuilder. I hope this tutorial saves you a lot of time in your game development life!

Please visit the forums to share your thoughts and questions about this tutorial and CocosBuilder!

This is a blog post by iOS Tutorial Team member Ali Hafizji, an iOS and Android developer working at Tavisca Solutions.

Ali Hafizji

Ali is an independent iOS and Android developer currently focussing on building immersive experiences on mobile devices. He is an avid programmer and loves learning better and faster ways of solving problems. You can follow him on Twitter or github.

User Comments

45 Comments

[ 1 , 2 , 3 ]
  • Hi , I followed your tutorial for using cocosbuilder for catJump.

    I have added button in custom scene . But when i am running my application in simulato, I am not able to see buttons in screen . :(
    akrant06
  • kovvuri sreenu wrote:hi
    its a gud tutorial but i got the same error above they mentioned "Assertion failure in -[CCScene addChild:]" ... how can i solve this error...





    Hi kovvuri , You are facing this issue due to this line

    Code: Select all
    [director runWithScene: [CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]];


    I also faced same issue and debugged it .
    You can remove ccbi file from your project and try to add it again . Clean your project and build it. it will solve the issue .
    akrant06
  • Just a quick question, why the game is running so slow when the first time you run it? it's like it has a huge lag that it seems about to crash. Is it because cocosbuilder is slow?
    a402192350
  • Followed the instructions. After Adding the MainMenuScene.ccbi compiled and Run the Xcode, But It shows Blank Black Screen with screen. Got no Idea whats the problem is.

    using Xcode 4.5
    Using Cocos Builder 2.1

    Help me
    RadhaKrishna
  • I am wondering how to make a background that scales to the screen size. Sprites don't scale to the screen size and I can't seem to add an image to the Layer.


    So, if I use multiple resolutions for a screen then I cannot setup a background that scales to those resolutions. Any suggestions on how to do that?

    Travis
    Metalboyblue
  • Hi,
    following through the tutorial pretty nicely until trying to compile after adding CCBReader and CCControlExtension. The file CCAnimationManager.m (in CCBReader) gives the following compiler errors:

    /Users/rrivas/Xcode4Projects/Cocos2D-Tutorial/CocosBuilder/CatJump/CatJump/CatJump/CCBReader/CCBAnimationManager.m
    /Users/rrivas/Xcode4Projects/Cocos2D-Tutorial/CocosBuilder/CatJump/CatJump/CatJump/CCBReader/CCBAnimationManager.m:527:15: Use of undeclared identifier 'target_'
    /Users/rrivas/Xcode4Projects/Cocos2D-Tutorial/CocosBuilder/CatJump/CatJump/CatJump/CCBReader/CCBAnimationManager.m:559:20: Use of undeclared identifier 'target_'; did you mean '_target'?
    /Users/rrivas/Xcode4Projects/Cocos2D-Tutorial/CocosBuilder/CatJump/CatJump/CatJump/CCBReader/CCBAnimationManager.m:566:3: Use of undeclared identifier 'target_'; did you mean '_target'?
    /Users/rrivas/Xcode4Projects/Cocos2D-Tutorial/CocosBuilder/CatJump/CatJump/CatJump/CCBReader/CCBAnimationManager.m:577:10: Use of undeclared identifier 'other'
    /Users/rrivas/Xcode4Projects/Cocos2D-Tutorial/CocosBuilder/CatJump/CatJump/CatJump/CCBReader/CCBAnimationManager.m:581:10: Use of undeclared identifier 'other'

    It looks like I could replace 'target_' with '_target' but I'm not sure if that's correct; furthermore, there's no such similar thing with 'other'.

    I'm using Cocos 2.1. Any ideas on what's going or suggestions for solving this are appreciated.

    Thanks!
    Phantom59
  • OK. I have a follow-up to my own post from August 16, 2013. The problem was caused by incompatibility between the latest released version of cocos2d (2.1) and CocosBuilder. The tutorial states to use the latest version of cocos2d. This would be v2.1 released, but this will not work. Thankfully, the author states the version he used which was v2.1 beta 4 which is available at https://github.com/cocos2d/cocos2d-iphone/releases. So, I downloaded this version used by the author and all is well.

    Hope this helps someone ...
    Phantom59
  • hi , i see the code
    but i hava a question.
    if ((CGRectIntersectsRect(catRect,carHood)) ||
    (CGRectIntersectsRect(catRect, vehicleRect))) {
    isCatColliding = YES;
    CCLOG(@"Collided with Red Car");
    }
    why use two CGRectIntersectsRect ?
    slashzl
  • The only thing I can think of is that the hood is lower; so the carHood rectangle is such that it is only hood. The vehicleRect is such that it is only the rest of the vehicle (not including the car hood or engine). If you only used one rectangle for the entire check then the kitty could loose a live by just being in the empty area in front of the windshield and above the car hood. This would not be fair to our kitty! :D

    Hope this helps!
    Phantom59
  • Hi,
    I finished the tutorial. I found it very useful. But in playing the game I noticed one problem: the kitty gets a 'dogde' counted even if he looses a life. That's just not fair! :)

    So I set out to remedy the situation with minimum changes to the existing code base. The lines after the comment ('Lets stop the vehicle ...") stops the vehicle animation which prevents the calling of the method carDone: (which is where the dodges are counted).

    Code: Select all
          if (isCatColliding) {
            // Play sound, take a hit, invincible, break out of loop
            [[SimpleAudioEngine sharedEngine] playEffect:@"squish.wav"];
            _invincible = true;
            [cat runAction:[CCSequence actions:
                            [CCBlink actionWithDuration:1.0 blinks:6],
                            [CCCallFunc actionWithTarget:self selector:@selector(doneInvincible)],
                            nil]];
            _lives--;
            [self setLives:_lives];
            if (_lives<=0) {
              [[CCDirector sharedDirector]replaceScene:[CCTransitionJumpZoom transitionWithDuration:1.0 scene:
                                                        [CCBReader sceneWithNodeGraphFromFile:@"GameOver.ccbi"]]];
            }
            // Lets stop the vehicle animation now. This will prevent a collision from counting as a dodge.
            // We'll also make the vehicle that collided blink just like the cat.
            [vehicle stopAllActions];
            _inCarCrash = TRUE;
            [vehicle runAction:[CCSequence actions:
                            [CCBlink actionWithDuration:1.0 blinks:6],
                                [CCCallFunc actionWithTarget:self selector:@selector(carCrashDone:)],
                            nil]];


    I still need to cleanup the vehicle sprite which is done is carCrashDone:.

    Code: Select all
    -(void) carCrashDone: (id) sender {
      CCSprite *vehicle = (CCSprite *)sender;
      [vehicle removeFromParentAndCleanup:YES];
      _inCarCrash = FALSE;
    }


    The variable _inCarCrash is used to make sure we don't start another vehicle until we're completely done with the present one.

    It's used here:

    Code: Select all
      double curTime = [[NSDate date]timeIntervalSince1970];
      if (curTime>_nextSpawn && !_inCarCrash) {
        int randomVehicle = arc4random() % 4;
       
        if (randomVehicle == kVehicleTypeRedCar) {


    and here:

    Code: Select all
      // Check for collisions
      if (!_invincible && !_inCarCrash) {
        float insetAmtX = 10;
        float insetAmtY = 10;
        BOOL isCatColliding;


    With these changes, dodges are only counted if the kitty indeed dodges the vehicle. Now we have an even playing field! ;)
    Phantom59
  • ok,thanks very much
    slashzl
  • Just a quick note - if you're trying to delete the default scene in CocosBuilder (now SpriteBuilder), you can use Ctrl-Backspace to do so.
    robotpukeko
  • Hi,

    I try to add the sprite that I created in CocosBuilder to a NSMutableArray. I named the sprite as "elephant" and chose "Doc root var" in CocosBuilder and declare a variable named "elephant" which is a CCSprite object in respective layer's header file. In .m file I tried to add it to a NSMutableArray and failed as that sprite is nil.

    What do I do wrong?
    mstfa07
  • Can you please update Cocos Builder Cat Jump tutorial to Sprite Builder targetting cross platform.
    Aliakber Hussain
[ 1 , 2 , 3 ]

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!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Non-Gaming!

    Loading ... Loading ...

Last week's winner: How to Make a Simple 2D Game with Metal.

Suggest a Tutorial - Past Results

Hang Out With Us!

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


Coming up in October: Xcode 6 Tips and Tricks!

Sign Up - October

Our Books

Our Team

Tutorial Team

  • Jack

... 52 total!

Update Team

  • Ray Fix

... 14 total!

Editorial Team

  • Alexis Gallagher

... 22 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

... 4 total!