How To Mask a Sprite with Cocos2D 1.0

Sometimes in your games you might find it handy to display only a portion of a sprite. One way you can do this is by using a second image called a mask. You set the mask image to be white wherever you want the image to show up – and transparent everywhere else. Then you […]

Version

Sometimes in your games you might find it handy to display only a portion of a sprite.

One way you can do this is by using a second image called a mask. You set the mask image to be white wherever you want the image to show up – and transparent everywhere else.

Then you can use the technique we’ll cover in the tutorial to combine the mask and the original image so that only the part you want comes through!

You’ll find this handy to accomplish a ton of interesting effects – such as creating a puzzle piece from a sprite, cutting out someone’s head to put it on a funny body, or to create a neat image frame effect – like we’ll be covering in this tutorial!

This tutorial will showing you how to mask a sprite in Cocos2D 1.0 using a handy class that comes with Cocos2D called CCRenderTexture.

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

Getting Started

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

Next, download the resources for this project and drag the folder to your Xcode project. Make sure that “Copy items into destination group’s folder (if needed)” is checked, and click Finish.

We’ll start things off with some jazz. 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 plays some cool music made by Kevin MacLeod and calls a new initializer we’re about to write.

In this scene we’re going to display a random calendar image (we have three to choose from). Here we’re storing the calendar number we’re displaying, and we modify the initializers to take the last calendar number shown as a parameter (so we can include some logic to not show the same calendar twice in a row).

This is just some basic Cocos2D code to display a random calendar image in the center of the screen. It also contains some basic logic so whenever you tap the screen, it makes a new scene with a neat bouncy transition.

Compile and run to try it out, and you should see a random calendar made by my lovely wife displayed each time you tap:

Now that we have a decent shell for our app, let’s implement the masking effect!

Masking and OpenGL Blend Modes

If you look at Art\CalendarMask.png in an image editor, you’ll see it looks like this:

We’re going to use this to mask our calendar image so it looks like a floaty piece of paper rather than a simple square. Wherever the image is transparent, we don’t want the image to show up, but wherever the image is opaque we do want it to show up.

To accomplish the effect we want, we’re going to take the following strategy:

We’re going to first draw the mask sprite, with the src color (the mask) set to GL_ONE and the destination color (an empty buffer) to GL_ZERO. So basically we’ll plop down the mask as-is.

We’ll then draw the calendar sprite, with the src color (the calendar) set to GL_DST_ALPHA. This is a fancy way of saying “look at the alpha value of what is currently in the buffer (the mask). Wherever it’s opaque, let the calendar show through, but where it’s transparent, don’t show anything!” The dst color (the mask) is set to GL_ZERO, so the existing mask drawn earlier goes away.

Awesome! You might think that you can just draw these two sprites one after another with Cocos2D and setting the blend modes appropriately and you’d be done – but you’d be wrong.

The problem is the above blending algorithm runs into problems if there’s something underneath the sprite you’re drawing – like a background, or another sprite. This is because it makes the assumption that the only thing in the image buffer after step 1 is the mask – no other image data.

So we need some way to have a “blank slate” when drawing this masked texture. And this is where our friend CCRenderTexture comes to the rescue!

Masking and CCRenderTexture

CCRenderTexture is a class that lets you draw to an offscreen buffer.

It is handy for a lot of reasons – you can use it to take screenshots of your game, efficiently cache user drawing, dynamically create sprite sheets at runtime, or what we’re going to use it for in this case – to help us mask a sprite.

To use CCRenderTexture, you take the following steps:

Create the CCRenderTextuer, specifying the width and height of the texture in pixels.

Call begin to initialize drawing to the CCRenderTexture.

Issue OpenGL commands to draw stuff – but it will go to the CCRenderTexture instead of the screen.

Call end when you are done, and CCRenderTexture will now have a sprite property with a texture you can use (but note it’s flipped upside down). Alternatively, you can add the CCRenderTexture directly as a child if you want.

Don’t freak out about step 3 – since you’re using Cocos2D, you usually don’t have to issue the OpenGL commands manually yourself! If you want to draw a node, all you have to do is call [myNode visit] and it will run all the appropriate OpenGL commands for you!

The only trick is the node needs to be positioned in the render texture’s coordinates. 0,0 is the bottom left of the render texture, so make sure that you position everything inside that space appropriately.

OK you’re probably sick of me blabbing on and are eager to see some code. I aim to please – let’s dive back in!

Masking a Sprite: The Implementation

Open up HelloWorldLayer.m and add the following method right above init:

This uses our new function to mask the calendar sprite, and adds the masked version to the scene.

Compile and run, and now you should see a masked sprite with Cocos2D 1.0!

Drawbacks of the CCRenderTexture Method

For this simple little app this works fine, but there are some drawbacks to this method you might start to notice for more complicated apps:

Creates an additional texture in memory each time you apply a mask. Texture memory is very limited on the iPhone so you have to be very careful about how many textures you have in memory at once. This is fine if you only need to mask a few textures at a time, but what if you have to mask 100?

Takes time to draw. Drawing with CCRenderTexture is not cheap (especially as your textures get larger). If you are doing this frequently, you might notice a performance hit.

Like I mentioned earlier, there is no way that I know of to get around these drawbacks with OpenGL ES 1.0. With OpenGL ES 2.0 you can mask much more efficiently with a shader – but that’s a topic for another tutorial!

Where To Go From Here?

Here is a sample project with all of the code from the above tutorial.

Check out Part 2 of the tutorial, we’ll get our first look at the new and exciting Cocos2D 2.0 branch, and write a custom shader to mask sprites!

In the meantime, if you have any questions or comments, please join the forum discussion below!

Contributors

Ray is part of a great team - the raywenderlich.com team, a group of over 100 developers and editors from across the world. He...

Author

Sometimes in your games you might find it handy to display only a portion of a sprite. One way you can do this is by using a second image called a mask. You set the mask image to be white wherever you want the image to show up – and transparent everywhere else. Then you […]