14
votes

how can i clip/crop/mask or just set the frame of a CCSprite in Cocos2D?

Something similar to: setting the frame for UIView, with clipping subviews = TRUE

My CCSprite Main Sprite have multiple Child Sprite added to it. I only want Mask part of that Main Sprite Sprite visible. Is there a way to clip or use a mask for CCSprite?

I could cut the background and layer that on top, leaving only that visible area, but is that the only way?!

here's a sample image demonstrating what I'm trying to achieve: alt text
(source: dnamique.com)

3

3 Answers

15
votes

I ended up using GL_SCISSOR.

in MainSprite I impemented:

- (void) visit
{
    if (!self.visible) {
        return;
    }
    glEnable(GL_SCISSOR_TEST);
    glScissor(x, y, width, height);   
    [super visit];
    glDisable(GL_SCISSOR_TEST);
}

This will clip or mask the specified area.

The only tricky bit is that in Landscape mode Cocos2D has 0,0 at the bottom-left side of the screen, while OpenGL has it at the bottom-right corner as it doesn't consider the orientation of the screen.

In other words, for OpenGL consider you have a rotated portrait Screen.

11
votes

I wrote a ClippingNode class which does exactly that. You can add other nodes (sprites, labels, etc.) to the ClippingNode and they will only be drawn in the region specified by the ClippingNode. It also takes device rotation into account.

Internally it uses GL_SCISSOR_TEST like in Bach's answer.

http://www.learn-cocos2d.com/2011/01/cocos2d-gem-clippingnode/

3
votes

I tried using Steffen Itterheim's ClippingNode, but was unable to get to work in a sufficiently robust enough fashion for my needs.

Believe it or not, the below code works fairly well and should be code complete. It handles device orientation changes, anchorPoint, position, scale (scaleX, scaleY). For cocos2d v2, you may just need to comment out the glPushMatrix and glPopMatrix calls..

To use, simply set the position and contentSize properties and add the child/children you want clipped to this ClippingNode instance. The contentSize property is used to define the dimensions of the clipping region.

example of usage:
ClippingNode *clipNode = [[ClippingNode alloc] init];
clipNode.anchorPoint = ccp(0.5f, 0);
clipNode.position = ccp(100, 25);
clipNode.contentSize = CGSizeMake(120, 120);

// add clipNode to your node hierarchy.
[parentNode addChild:clipNode];

// add one or more children to your clipNode:
[clipNode addChild:child1];

// ClippingNode.h
// CC0 - (public domain. Use in anyway you see fit.)
// No warranty of any kind is expressed or implied.
//
// by UChin Kim.
//
// the caller can simply set the regular cocos2d
// properties: position and contentSize to define the clipping region implicitly (i.e. the
// position and contentSize of the ClippingNode is the clipping region to be used).
// as an added bonus, position seems to work as expected (relative to parent node, instead of
// requiring absolute positioning).
//
// also, anchorPoint and scale properties seem to work as expected as well..
// no special code is neeed to handle device orientation changes correctly..
//
// To visually see exactly what is being clipped, set the following #define
// #define SHOW_CLIPPED_REGION_IN_LIGHT_RED 1
//

#import "cocos2d.h"

@interface ClippingNode : CCNode

@end

//
// ClippingNode.m
//
#import "ClippingNode.h"

@implementation ClippingNode

-(void) visit
{
CGPoint worldOrg = [self convertToWorldSpace:ccp(0, 0)];
CGPoint dest = [self convertToWorldSpace:ccp(self.contentSize.width, self.contentSize.height)];
CGPoint dims = ccpSub(dest, worldOrg);

glPushMatrix();
glEnable(GL_SCISSOR_TEST);

glScissor(worldOrg.x, worldOrg.y, dims.x, dims.y);

#if SHOW_CLIPPED_REGION_IN_LIGHT_RED
glColor4ub(64, 0, 0, 128);
ccDrawSolidRect(ccp(0, 0), ccp(1024, 1024));
#endif

[super visit];

glDisable(GL_SCISSOR_TEST);
glPopMatrix();
}

@end