PeqNP
PeqNP

Reputation: 1361

Clip a sprite using Cocos2d-x

I can not clip a simple sprite. I am using Cocos2d-x 3.1rc0. Here is the code.

local sprite = cc.Sprite:create("red-box.png")
local shape = cc.DrawNode:create()
shape:drawPolygon({cc.p(0, 0), cc.p(0, 100), cc.p(100, 100), cc.p(100, 0), cc.p(0, 0)}, 5, cc.c4b(0, 0, 0, 1), 0, cc.c4b(0, 0, 0, 1))
local clipper = cc.ClippingNode:create(shape)
clipper:setContentSize(sprite:getContentSize())
clipper:setScale(0.25)
clipper:setGlobalZOrder(20)
clipper:setPosition(10, 10)
clipper:setInverted(true)
clipper:addChild(sprite)
gameLayer:addChild(clipper)

This follows the same pattern as used in questions:

Here is an image describing the issue:

enter image description here

I've tried a variety of different combinations such as setting clipper:setAlphaThreshold(0). I even went so far to try and implement this person's code:

By the way, the depthFormat is set to GL_DEPTH24_STENCIL8_OES. So that's not the problem.

Also, why does the drawing of the stencil start in the middle and not the bottom left? I set the anchorPoint of the clipper and shape and could not get it to move. Confused.

Here is my AppController.mm code:

window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
                                 pixelFormat: kEAGLColorFormatRGBA8
                                 depthFormat: GL_DEPTH24_STENCIL8_OES
                          preserveBackbuffer: NO
                                  sharegroup: nil
                               multiSampling: NO
                             numberOfSamples: 0 ];

[eaglView setMultipleTouchEnabled:YES];
// Use RootViewController manage CCEAGLView
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.wantsFullScreenLayout = YES;
viewController.view = eaglView;

// Set RootViewController to window
if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
    // warning: addSubView doesn't work on iOS6
    [window addSubview: viewController.view];
}
else
{
    // use this method on ios6
    [window setRootViewController:viewController];
}

[window makeKeyAndVisible];

[[UIApplication sharedApplication] setStatusBarHidden: YES];

// IMPORTANT: Setting the GLView should be done after creating the RootViewController
cocos2d::GLView *glview = cocos2d::GLView::createWithEAGLView(eaglView);
cocos2d::Director::getInstance()->setOpenGLView(glview);

cocos2d::Application::getInstance()->run();
return YES;

Upvotes: 0

Views: 1880

Answers (1)

PeqNP
PeqNP

Reputation: 1361

Here is my solution. I use this code to fill a progress bar.

    local size = cc.Director:getInstance():getVisibleSize()
    local origin = cc.Director:getInstance():getVisibleOrigin()
    local empty = cc.Sprite:createWithSpriteFrame(cc.SpriteFrameCache:getInstance():getSpriteFrame(_frameName))
    -- Put the frame of the progress bar completely in view. 
    local bbox = empty:getBoundingBox()
    empty:setPosition(bbox.width/2, bbox.height/2)

    -- Background layer of frame (a nice transparent background)
    local bg = createSpriteWithOffset(_bgName, bbox)
    -- The layer that will fill the progress bar
    local fill = createSpriteWithOffset(_fillName, bbox)
    -- Text texture displayed over the progress bar
    local text = cc.Sprite:createWithSpriteFrame(cc.SpriteFrameCache:getInstance():getSpriteFrame(_textName))
    local tbbox = text:getBoundingBox()
    text:setPosition(tbbox.width+70+bbox.x, (tbbox.height*0.5)+bbox.y+bbox.height) -- Left adjust on top

    -- Stencil (masking layer)
    local mask = cc.c4b(0, 0, 0, 1)
    -- Number of fill pixels to show.
    local fillAmt = bbox.width * percent
    local polygon = {cc.p(0, 0), cc.p(0, bbox.height), cc.p(fillAmt, bbox.height), cc.p(fillAmt, 0), cc.p(0, 0)}
    local stencil = cc.DrawNode:create()
    stencil:drawPolygon(polygon, 5, mask, 0, mask)
    local clipper = cc.ClippingNode:create(stencil)
    local paper = cc.ClippingNode:create(stencil)
    paper:addChild(fill)
    paper:addChild(clipper)

    local render = cc.RenderTexture:create(bbox.width, bbox.height+(tbbox.height*0.5), cc.TEXTURE2_D_PIXEL_FORMAT_RGB_A8888, gl.DEPTH24_STENCIL8_OES)
    render:begin()
    render:addChild(bg)
    render:addChild(paper)
    render:addChild(empty)
    render:addChild(text)
    bg:visit()
    paper:visit()
    empty:visit()
    text:visit()
    render:endToLua()
    local sprite = cc.Sprite:createWithTexture(render:getSprite():getTexture())
    sprite:setFlippedY(true)
    return sprite

Here is a picture of the final result.

enter image description here

The trick is how the stencil is created.

Always keep in mind that your drawing starts at 0,0; which makes sense right? Also, a few pointers; It is better to scale the rendered texture rather than scaling the textures being rendered before you render the image. If you scale your textures down to size before rendering, the quality of the render will be low. Lastly, I find it much easier to render the graphic at 0,0, rather than the positioning the elements to where you want them to be after the render takes place. It makes positioning the final result much easier.

Upvotes: 1

Related Questions