Search code examples
xcodecocos2d-iphonescalecclayer

Cocos2d how to scale a sprite without scaling layer? Or, How to scale and crop sprite/layer?


iPad App setup: SceneA contains layerA - 1024x768. Push a button in layerA, layerB drops down over top using a CCMoveTo action. LayerB is only 800x600 so you can see layerA behind it (think of an overlayed pause screen type effect). LayerB contains an 800x600 sprite that the user can zoom in on by pressing a button. The zoom effect is simply a combination of CCScaleTo and CCMoveTo to keep it centered on the part it's zooming in on. However, when the sprite scales, so does layerB overtop of layerA. Is there a way to scale the sprite within a contained window?


Solution

  • LayerB should use the GL_SCISSOR_TEST to trim the outside of itself. You can easily google for more information about it, it basically defines a rect and then uses glScissoron it to remove the outside. I have a class I extend when I need to do this, that goes as follows:

    //
    //  CCNodeClip.h
    //
    //  Created by Ignacio Orlandoni on 7/29/11.
    //
    
    #import <Foundation/Foundation.h>
    #import "cocos2d.h"
    @interface CCNodeClip : CCLayer {
    
    }
    
    -(void)preVisit;
    -(void)postVisit;
    
    @end
    

    -

    //
    //  CCNodeClip.m
    //
    //  Created by Ignacio Orlandoni on 7/29/11.
    //
    
    #import "CCNodeClip.h"
    @implementation CCNodeClip
    
    -(void)visit {
        [self preVisit];
        [super visit];
        [self postVisit];
    }
    
    -(void)preVisit {
        if (!self.visible)
            return;
    
        glEnable(GL_SCISSOR_TEST);
    
        CGPoint position = [self position];
    
        //I don't remember if this rect really serves for both orientations, so you may need to change the order of the values here.
        CGRect scissorRect = CGRectMake(position.x, position.y, [self contentSize].width, [self contentSize].height);
    
        //    CCLOG(@"Scrissor Rect: X: %02f, Y:%02f, W: %02f, H: %02f", scissorRect.origin.x,     scissorRect.origin.y, scissorRect.size.width, scissorRect.size.height);
    
        // Handle Retina
        scissorRect = CC_RECT_POINTS_TO_PIXELS(scissorRect);
    
        glScissor((GLint) scissorRect.origin.x, (GLint) scissorRect.origin.y,
                  (GLint) scissorRect.size.width, (GLint) scissorRect.size.height);
    }
    
    -(void)postVisit {
        glDisable(GL_SCISSOR_TEST);
    }
    
    
    @end 
    

    With that imported into LayerB, you can now define it as a CCNodeClip instead of CCLayer.

    Some links...

    glScissor << cocos2d Forum

    Circle shape clipping with opengl-es in cocos2d << StackOverflow

    Cocos2d iPhone - Sprite cliping/mask/frame << StackOverflow

    Another Cocos2D gem: ClippingNode << Learn-Cocos2d.com

    As a side note...

    CCScaleTo + CCMoveTo can be avoided if the anchor point for the sprite is centered, so the image stays centered in the container as it scales. (.anchorPoint = ccp(0.5, 0.5);)