2012-11-21 12 views
5

Ich brauche einen Umriss wie diese dynamisch zu erstellen:Outline (Hub) für einen nicht rechteckigen CCNode in cocos2d

Red 2px outline

Nicht für einen CCSprite, sondern für mehrere animierte CCSprites in einem CCNode vereint. Ich denke an:

  1. Kopieren CCNode Inhalt auf eine Textur (wie canvasBitmapData.draw(sourceDisplayObject) in AS3)
  2. Schaffung CCSprite mit der resultierenden Textur
  3. das Sprite Abtönfarbe zu skizzieren und es auf ein bisschen
  4. Skalieren
  5. das Sprite hinter anderen Sprites in dem Knoten platzieren

ich habe keine Ahnung, wie 1. Schritt auszuführen Und vielleicht ist es schneller „true stroke“ ist rund um die Textur zu zeichnen undurchsichtigen Pixel statt Tönung in Schritt 3?

+0

Keine Option ein mit dem Hub png zu machen? – Setrio

+0

@Setrio: es ist. Ich habe verschiedene Möglichkeiten ausprobiert (wie oben angedeutet oder sogar mit einem Shader - um die Farbe des Umrisses ändern zu können) ... aber die resultierenden Rahmen waren enttäuschend wegen der Alias-Grenze zwischen dem Umriss und dem Hintergrund und zwischen dem Umriss und dem Sprite . Ich habe es mit meinem trustee photoshop gemacht, mit Anti-Aliasing-Funktionen. – YvesLeBorg

+0

@Setrio: Nein, stellen Sie sich vor, die Hände des Beispiels wären separate Sprites und drehen sich während der Animationen. Ich weiß über vorbereitete Sprite-Blätter, aber das Spiel ist RPG mit vielen veränderbaren animierten Teilen der Helden. Und der Strich ist sehr wichtig für den Stil (Beispiel Sprite ist nicht aus dem Spiel). –

Antwort

2

ich total vergessen haben, eine Antwort auf diese Frage zu stellen. Hier ist der Code für einen sehr weichen Strich. Es ist nicht schnell, aber funktionierte großartig für ein paar große Sprites auf dem ersten iPad.

Die Idee besteht darin, winzige farbige und unscharfe Kugeln um das Sprite zu zeichnen und sie auf ihre eigene Textur zu legen. Es kann sowohl für CCNode als auch für CCSprite verwendet werden. Der Code verschiebt auch Ankerpunkte, da die resultierenden Sprites eine etwas größere Breite und Höhe haben.

resultierende Umriss (Körper und zwei Hände, etwa 0,3 s auf iPad1):

outline

Weiße Kugeln Beispiele:

CCNode Kategorie, für Cocos2d-iPhone 2.1:

@implementation CCNode (Outline) 

- (CCSprite*) outline 
{ 
    return [self outlineRect:CGRectMake(0, 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, 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; 
} 
+0

Was ist CCTools? Beziehen Sie sich auf ein eigenes Utility? –

+0

@ KeremBaydoğan oops, sorry, ja. Es wurde der Antwort hinzugefügt. –

1

Ich habe eine allgemeine Funktion, die ich aus verschiedenen Quellen aufgebaut habe (ich schäme mich zu sagen, dass ich hier nicht verweisen kann). Was es tut, ist ein CCSprite, erstellen Sie einen Strich, den Sie hinter ihn setzen und in einem CCRenderTexture zurückgeben können. Wenn das Sprite, das übergeben wird, Kinder hat (wie es dir möglich ist), sehe ich keinen Grund, warum es nicht tun würde, was du willst, aber ich habe es nicht versucht.

Hier ist es im Fall, dass es funktioniert:

@implementation Cocosutil 

+(CCRenderTexture*) createStrokeForSprite:(CCSprite*)sprite size:(float)size color:(ccColor3B)cor 
{ 
    CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:sprite.texture.contentSize.width+size*2 height:sprite.texture.contentSize.height+size*2]; 
    CGPoint originalPos = [sprite position]; 
    ccColor3B originalColor = [sprite color]; 
    BOOL originalVisibility = [sprite visible]; 
    [sprite setColor:cor]; 
    [sprite setVisible:YES]; 
    ccBlendFunc originalBlend = [sprite blendFunc]; 
    [sprite setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }]; 
    CGPoint bottomLeft = ccp(sprite.texture.contentSize.width * sprite.anchorPoint.x + size, sprite.texture.contentSize.height * sprite.anchorPoint.y + size); 
    CGPoint positionOffset = ccp(sprite.texture.contentSize.width * sprite.anchorPoint.x - sprite.texture.contentSize.width/2,sprite.texture.contentSize.height * sprite.anchorPoint.y - sprite.texture.contentSize.height/2); 
    CGPoint position = ccpSub(originalPos, positionOffset); 

    [rt begin]; 
    for (int i=0; i<360; i+=30) 
    { 
     [sprite setPosition:ccp(bottomLeft.x + sin(CC_DEGREES_TO_RADIANS(i))*size, bottomLeft.y + cos(CC_DEGREES_TO_RADIANS(i))*size)]; 
     [sprite visit]; 
    } 
    [rt end]; 
    [sprite setPosition:originalPos]; 
    [sprite setColor:originalColor]; 
    [sprite setBlendFunc:originalBlend]; 
    [sprite setVisible:originalVisibility]; 
    [rt setPosition:position]; 
    return rt; 
} 

@end 

und hier ist der Code, wo ich es verwenden:

- (id) initWithSprite:(CCSprite*)sprite color:(ccColor3B)color strokeSize:(float)strokeSize strokeColor:(ccColor3B)strokeColor { 
    self = [super init]; 

    if (self != nil) { 
     strokeColor_ = strokeColor; 
     strokeSize_ = strokeSize; 
     CCRenderTexture *stroke = [CocosUtil createStrokeForSprite:sprite size:strokeSize color:strokeColor]; 
     [self addChild:stroke z:kZStroke tag:kStroke]; 
     [self addChild:sprite z:kZLabel tag:kLabel]; 
     [self setContentSize:[sprite contentSize]]; 
    }  

    return self; 
}