cocos2d-iphone – cocos2d中非矩形CCNode的轮廓(笔划)

我需要动态创建这样的轮廓:

不适用于CCSprite,而是适用于在一个CCNode中合并的多个动画CCSprit.我在考虑:

>将CCNode的内容复制到纹理(如AS3中的canvasBitmapData.draw(sourceDisplayObject))
>使用生成的纹理创建CCSprite
>将精灵着色为轮廓颜色并稍微缩放
>将精灵放在节点中的其他精灵后面

我不知道如何执行步骤1.也许在步骤3中在纹理的不透明像素周围绘制“true stroke”而不是色调比例更快?

我完全忘记发布这个问题的答案.这是一个非常平滑的笔画的代码.它并不快,但在第一台iPad上为几个大精灵工作得很好.

我们的想法是在精灵周围画出微小的彩色和模糊的球,并将它们放在自己的纹理上.它既可以用于CCNode,也可以用于CCSprite.该代码还会移动锚点,因为生成的精灵将具有更大的宽度和高度.

得到的轮廓(身体和2只手,在iPad1上约0.3秒):

白球示例:

> 5f:http://i.stack.imgur.com/e9kos.png
> 10f:http://i.stack.imgur.com/S5goU.png
> 20f:http://i.stack.imgur.com/qk7GL.png

CCNode类别,适用于Cocos2d-iPhone 2.1:

@implementation CCNode (Outline)

- (CCSprite*) outline
{
    return [self outlineRect:CGRectMake(0,self.contentSize.width,self.contentSize.height)];
}

- (CCSprite*) outlineRect:(CGRect)rect
{
    NSInteger gap = dscale(4);
    CGPoint positionShift = ccp(gap - rect.origin.x,gap - rect.origin.y);
    CGSize canvasSize = CGSizeMake(rect.size.width + gap * 2,rect.size.height + gap * 2);

    CCRenderTexture* renderedSpriteTexture = [self renderTextureFrom:self shiftedFor:positionShift onCanvasSized:canvasSize];
    CGSize textureSize = renderedSpriteTexture.sprite.contentSize;
    CGSize textureSizeInPixels = renderedSpriteTexture.sprite.texture.contentSizeInPixels;

    NSInteger bitsPerComponent = 8;
    NSInteger bytesPerPixel = (bitsPerComponent * 4) / 8;
    NSInteger bytesPerRow = bytesPerPixel * textureSizeInPixels.width;
    NSInteger myDataLength = bytesPerRow * textureSizeInPixels.height;

    NSMutableData* buffer = [[NSMutableData alloc] initWithCapacity:myDataLength];
    Byte* bytes = (Byte*)[buffer mutableBytes];

    [renderedSpriteTexture begin];
    glReadPixels(0,textureSizeInPixels.width,textureSizeInPixels.height,GL_RGBA,GL_UNSIGNED_BYTE,bytes);
    [renderedSpriteTexture end];

    //SEE ATTACHMENT TO GET THE FILES
    NSString* spriteFrameName;
    if (IS_IPAD) spriteFrameName = (CC_CONTENT_SCALE_FACTOR() == 1) ? @"10f.png" : @"20f.png";
    else spriteFrameName = (CC_CONTENT_SCALE_FACTOR() == 1) ? @"5f.png" : @"10f.png";

    CCSprite* circle = [CCSprite spriteWithSpriteFrameName:spriteFrameName];
    circle.anchorPoint = ccp(0.48,0.48);
    float retinaScale = (CC_CONTENT_SCALE_FACTOR() == 1) ? 1.0 : 0.5;

    CCRenderTexture* strokeTexture = [CCRenderTexture renderTextureWithWidth:textureSize.width height:textureSize.height pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
    [strokeTexture beginWithClear:0 g:0 b:0 a:0];
    for (NSInteger x = 0; x < textureSizeInPixels.width; x++)
    {
        for (NSInteger y = 0; y < textureSizeInPixels.height; y++)
        {
            NSInteger idx = y * bytesPerRow + x * bytesPerPixel + 3;
            NSInteger w = 1;
            if (bytes[idx] <= 254)
            {
                BOOL shouldBeStroked = NO;
                for (NSInteger nx = -w; nx <= w; nx++)
                {
                    for (NSInteger ny = -w; ny <= w; ny++)
                    {
                        if (x + nx < 0 || y + ny < 0 || x + nx >= textureSizeInPixels.width || y + ny >= textureSizeInPixels.height)
                            continue;

                        if (bytes[idx + nx * bytesPerPixel + ny * bytesPerRow] == 255)
                        {
                            shouldBeStroked = YES;
                            break;
                        }
                    }
                }

                if (shouldBeStroked == YES)
                {
                    circle.position = ccp(x * retinaScale,y * retinaScale);
                    [circle visit];
                }
            }
        }
    }
    [strokeTexture end];

    CCSprite* resultSprite = [CCSprite spriteWithTexture:strokeTexture.sprite.texture];
    [resultSprite.texture setAntiAliasTexParameters];
    resultSprite.flipY = YES;

    if ([self isKindOfClass:[CCSprite class]]) {
        CGPoint oldAnchorInPixels = ccp(roundf(self.contentSize.width * self.anchorPoint.x),roundf(self.contentSize.height * self.anchorPoint.y));
        resultSprite.anchorPoint = ccp((oldAnchorInPixels.x + gap) / resultSprite.contentSize.width,(oldAnchorInPixels.y + gap) / resultSprite.contentSize.height);
        resultSprite.position = self.position;
    } else { //CCNode
        resultSprite.anchorPoint = CGPointZero;
        resultSprite.position = ccpAdd(self.position,ccp(rect.origin.x - gap,rect.origin.y - gap));
    }
    return resultSprite;
}

- (CCRenderTexture*) renderTextureFrom:(CCNode*)node shiftedFor:(CGPoint)posShift onCanvasSized:(CGSize)size
{
    SoftAssertion(!CGSizeEqualToSize(size,CGSizeZero),@"node has zero size");

    BOOL isSprite = [node isMemberOfClass:[CCSprite class]];
    CGPoint apSave = node.anchorPoint;
    CGPoint posSave = node.position;
    BOOL wasVisible = node.visible;

    CCRenderTexture* rtx = [CCRenderTexture renderTextureWithWidth:size.width
                                                            height:size.height
                                                       pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
    [rtx beginWithClear:0 g:0 b:0 a:0];

    node.anchorPoint = CGPointZero;
    node.position = posShift;
    node.visible = YES;

    if (isSprite) [node visit];
    else [[self cloneCCNode:node] visit];

    node.anchorPoint = apSave;
    node.position = posSave;
    node.visible = wasVisible;

    [rtx end];
    return rtx;
}

- (CCNode*) cloneCCNode:(CCNode*)source
{
    CCNode* clone = [CCNode node];

    void (^copyCCNodeProperties)(CCNode*,CCNode*) = ^(CCNode* source,CCNode* clone)
    {
        clone.visible = source.visible;
        clone.rotation = source.rotation;
        clone.position = source.position;
        clone.anchorPoint = source.anchorPoint;
        clone.zOrder = source.zOrder;
        clone.tag = source.tag;
    };

    for (CCNode* srcSubnode in source.children) {

        CCNode* subNode;

        if ([srcSubnode isMemberOfClass:[CCSprite class]]) {
            CCSprite* srcSprite = (CCSprite*)srcSubnode;
            subNode = [CCSprite spriteWithTexture:srcSprite.texture];
            CCSprite* subSprite = (CCSprite*)subNode;
            subSprite.flipX = srcSprite.flipX;
            subSprite.flipY = srcSprite.flipY;
            subSprite.displayFrame = srcSprite.displayFrame;
            subSprite.opacity = srcSprite.opacity;
        }
        else if ([srcSubnode isMemberOfClass:[CCLabelTTF class]]) {
            CCLabelTTF* srcLabel = (CCLabelTTF*)srcSubnode;
            subNode = [CCLabelTTF labelWithString:srcLabel.string fontName:srcLabel.fontName fontSize:srcLabel.fontSize dimensions:srcLabel.dimensions hAlignment:srcLabel.horizontalAlignment vAlignment:srcLabel.verticalAlignment];
            CCSprite* subLabel = (CCSprite*)subNode;
            subLabel.flipX = srcLabel.flipX;
            subLabel.flipY = srcLabel.flipY;
            subLabel.color = srcLabel.color;
        }
        else {
            subNode = [self cloneCCNode:srcSubnode];
        }

        copyCCNodeProperties(srcSubnode,subNode);
        [clone addChild:subNode];
    }
    copyCCNodeProperties(source,clone);

    return clone;
}

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...