How To Mask a Sprite with Cocos2D 2.0

In the previous tutorial, we showed you how to mask a sprite with Cocos2D 1.0. This method worked OK, but it had some drawbacks – it bloated our texture memory and had a performance hit for the drawing. But with Cocos2D 2.0 and OpenGL ES 2.0, we can do this much more efficiently by writing […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share

Learn how to mask a sprite with a custom fragment shader and Cocos2D 2.0!

Learn how to mask a sprite with a custom fragment shader and Cocos2D 2.0!

Learn how to mask a sprite with a custom fragment shader and Cocos2D 2.0!

In the previous tutorial, we showed you how to mask a sprite with Cocos2D 1.0.

This method worked OK, but it had some drawbacks – it bloated our texture memory and had a performance hit for the drawing.

But with Cocos2D 2.0 and OpenGL ES 2.0, we can do this much more efficiently by writing a custom shader!

This tutorial will be our first foray into the new and exciting Cocos2D 2.0 branch. You’ll learn how to get started with the new Cocos2D 2.0 branch, and how to write a custom shader for a CCNode!

To fully understand this tutorial, it will really help to have some basic understanding of OpenGL ES 2.0 first. If you are new to OpenGL ES 2.0, check out the OpenGL ES 2.0 for iPhone tutorial series first.

Without further ado, let’s get masking with shaders!

Introducing Cocos2D 2.0 Branch

Cocos2D 2.0 is a major new version of Cocos2D that uses OpenGL ES 2.0 instead of OpenGL ES 1.0. This means some older devices that don’t support OpenGL ES 2.0 can’t run apps made with thie branch, but most devices are OpenGL ES 2.0 compatible these days, so it’s becoming more and more of a decent business decision to use it.

Because even though some older devices can’t run it – it gives us lots of cool new things to play with, primarily OpenGL ES 2.0 shaders! And as you’ll see, using shaders makes masking much more efficient.

The Cocos2D 2.0 branch is still under active development, but is now available for early adopters.

And that means us! :]

The only way to get the Cocos2D 2.0 branch is by grabbing it from the git repository, so if you don’t have git installed already, download it here.

Then go to Applications\Utilities, and click on your Terminal app. Navigate to a directory of your choosing, and then issue the following command:

git clone https://github.com/cocos2d/cocos2d-iphone.git

Once it finishes downloading, switch to the cocos2d-iphone directory and check out the Cocos2D 2.0 branch (called gles20) as follows:

cd cocos2d-iphone
git checkout gles20

Next, go ahead and install the new Cocos2D 2.0 templates. This will overwrite your current Cocos2D templates, but that’s OK – personally I just keep the 1.0 and 2.0 directories somewhere handy, and run install-templates.sh again from whichever version of Cocos2D I need the templates from.

./install-templates.sh -f -u

Back in Xcode, go to File\New\New Project, choose iOS\cocos2d\cocos2d, and click Next. Name the new project MaskedCal2, click Next, choose a folder to save the project in, and click Create.

If you try to Compile and Run, you’ll see… a blank screen?

This threw me for a loop the first time I was playing around with the new branch. Luckily the solution is simple – the templates don’t include some required files you need.

To add the required files, open the cocos2d-ios.xcodeproj from where you downloaded the github repository, and drag the Resources\Shaders folder into your Xcode project.

Very important: When you drag the folder over, make sure that “Copy items into destination group’s folder” is selected, and “Create folder references for any added folders” is selected. By selecting the “folder references” option, the shaders files will be copied into a subdirectory of your bundle, which is where Cocos2D is looking.

Compile and run, and you should see a Hello World for Cocos2D 2.0 with OpenGL ES 2.0!

Hello, Cocos2D 2.0!

Hello, Cocos2D 2.0!

Hello, Cocos2D 2.0!

Creating a Simple Cocos2D 2.0 Project

Let’s get our project set up to just cycle through the list of calendar images like we did before, before we turn to masking.

Like you did before, drag the resources for this project into your Xcode project. Make sure that “Copy items into destination group’s folder (if needed)” is checked and “Create groups for any added folders” is selected, and click Finish.

Open up AppDelegate.m and make the following changes:

// Add to top of file
#import "SimpleAudioEngine.h"

// At end of applicationDidFinishLaunching, replace last line with the following 2 lines:
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"TeaRoots.mp3" loop:YES];
[[CCDirector sharedDirector] runWithScene: [HelloWorldLayer sceneWithLastCalendar:0]]; 

This is the same as last time.

Then open up RootViewController.m and inside shouldAutorotateToInterfaceOrientation, replace the return YES with the following:

return ( UIInterfaceOrientationIsLandscape( interfaceOrientation ) );

This sets up the app to only support landscape mode (not sure why it’s not set up that way in the Cocos2D 2.0 templates).

Next open up HelloWorldLayer.h make the following changes:

// Add new instance variable
int calendarNum;

// Replace the +(CCScene*) scene declaration at the bottom with the following:
+ (CCScene *) sceneWithLastCalendar:(int)lastCalendar;
- (id)initWithLastCalendar:(int)lastCalendar;

This is also the same as last time.

Finally make the following changes to HelloWorldLayer.m:

// Replace +(CCScene *) scene with the following
+(CCScene *) sceneWithLastCalendar:(int)lastCalendar // new
{
    CCScene *scene = [CCScene node];
    HelloWorldLayer *layer = [[[HelloWorldLayer alloc] 
        initWithLastCalendar:lastCalendar] autorelease]; // new
    [scene addChild: layer];	
    return scene;
}

// Replace init with the following
-(id) initWithLastCalendar:(int)lastCalendar
{
	if( (self=[super init])) {
        
        CGSize winSize = [CCDirector sharedDirector].winSize;
        
        do {
            calendarNum = arc4random() % 3 + 1;
        } while (calendarNum == lastCalendar);
        
        NSString * spriteName = [NSString 
            stringWithFormat:@"Calendar%d.png", calendarNum];
        
        // BEGINTEMP
        CCSprite * cal = [CCSprite spriteWithFile:spriteName];        
        cal.position = ccp(winSize.width/2, winSize.height/2);        
        [self addChild:cal];
        // ENDTEMP
        
        self.isTouchEnabled = YES;
	}
	return self;
}

// Add new methods
- (void)registerWithTouchDispatcher {
    [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self 
        priority:0 swallowsTouches:YES];
}

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    CCScene *scene = [HelloWorldLayer sceneWithLastCalendar:calendarNum];
    [[CCDirector sharedDirector] replaceScene:
        [CCTransitionJumpZoom transitionWithDuration:1.0 scene:scene]];
    return TRUE;
}

Yet again – same as last time (except the begintemp/endtemp positions slightly changed). So you see, the user API didn’t really change much in Cocos2D 2.0 – it just has some extra awesome features you’re about to see :]

Compile and run, and the app should display the cycling list of calendars like before, but using Cocos2D 2.0 and OpenGL ES 2.0 now!

A non masked image in Cocos2D 2.0

Contributors

Over 300 content creators. Join our team.