taoru's memo

Objective-c,cocos2d,など開発についてのメモ(andoroidも少しだけ)

CCRenderTextureで輪郭線を描く

cocos2dを使っていて、CCSpriteに輪郭線を付けたい!ということがよくある。

「輪郭線」「Outline」「Outline Stroke」などでググると、色々出てくるけどCCRenderTextureを使う方法がメジャーみたい。
ということで試してみた。

CCSpriteとCCLableTTF(CCSpriteのsubclass)ができればよかったので、CCSpriteのCategoryで作ってみた。

#import "CCSprite+OutlineStroke.h"

@implementation CCSprite (OutlineStroke)

- (void)createOutlineWithStroke:(float)stroke color:(ccColor3B)color
{
    CGPoint center = ccp(self.texture.contentSize.width  * self.anchorPoint.x + stroke,
                         self.texture.contentSize.height * self.anchorPoint.y + stroke);
    float factor = [self isKindOfClass:[CCLabelTTF class]] ? CC_CONTENT_SCALE_FACTOR() : 1.0f;
    CCRenderTexture *render = [CCRenderTexture renderTextureWithWidth:self.texture.contentSize.width * factor + stroke*2
                                                               height:self.texture.contentSize.height* factor + stroke*2];
    ccColor3B   origColor   = self.color;
    CGPoint     origAnchor  = self.anchorPoint;
    CGPoint     origPos     = self.position;
    float       origScale   = self.scale;
    ccBlendFunc origBlend   = self.blendFunc;
    
    [self setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
    
    NSInteger drawNum   = 16; // 描画回数, この数値を大きくするとより滑らかになるが遅くなる
    
    [render begin];
    
    self.color          = color;
    self.scaleY         *= -1;     // CCRenderTextureは上下反転するのでFlipさせる
    
    // オリジナルの周囲にstrokeずつずらして描画
    float radian = 0.0;
    for (int i=0; i<drawNum; i++) {
        self.position = ccpAdd(center, ccp(cosf(radian) * stroke, sinf(radian) * stroke));
        [self visit];
        radian += CC_DEGREES_TO_RADIANS(360.0f/drawNum);
    }
    
    // オリジナルを中心に描画
    self.blendFunc  = origBlend;
    self.color      = origColor;
    self.position   = center;
    [self visit];
    
    [render end];
    
    self.anchorPoint    = origAnchor;
    self.position       = origPos;
    self.scale          = origScale;
    
    // アンチエイリアスに設定
    // この設定をしないと、ジャギーが目立つ
    [render.sprite.texture setAntiAliasTexParameters];
    
    // テクスチャをアウトライン付きのものと入れ替える
    self.texture = render.sprite.texture;
    self.textureRect = CGRectMake(render.sprite.textureRect.origin.x,
                                  render.sprite.textureRect.origin.y,
                                  render.sprite.textureRect.size.width / factor,
                                  render.sprite.textureRect.size.height / factor);
}


こんな感じ


イイね!

textureを入れ替えちゃう破壊的メソッドになっているので、
返り値をCCTexture2DかCCSpriteに書き換えて自分でセットする方が安全で使いやすいかも。

CCLabelTTFのときにだけvisitするときに、大きさが倍になってしまう。なんでやねん。
対処するために、factorという変数を用意してrenderTextureのサイズを合わせている。



もしこれを動的にやるなら、CCRenderTextureは使わずに
visitをオーバーライドして同じことをさせればできると思う。
その方法ならサイズは関係ないし、CCNodeでもできそう。


参考:Font Stroke cocos2d for iPhone

QLOOKアクセス解析