taoru's memo

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

CCNodeのカテゴリで、他のCCNodeをゆったり追うSoftFollowというのを作ってみた その3

CCNodeのカテゴリで、他のCCNodeをゆったり追うSoftFollowというのを作ってみた その2 - 追記2/26 - taoru's memo
↑の続編になります。

今回はタイトル詐欺になるけど、カテゴリではなくCCActionを継承してTTSoftFollowを作った。
prefixどうしようかな〜と思って、taoru toolsという意味でTTをつけてみました。なんか恥ずかしい。

TTSoftFollow.h

#import "CCNode.h"
#import "CCAction.h"

@interface TTSoftFollow : CCAction <NSCopying>

/**
 *  followedNode: 追いかける対象Node
 *  strength    : 引きよせる強さ。最大値は"1" 小さいほどゆったり追いかける。
 *  distance    : 対象と一定の距離を保ちたい場合に指定
 */
+ (id)actionWithTarget:(CCNode *)followedNode;
+ (id)actionWithTarget:(CCNode *)followedNode strength:(float)strength;
+ (id)actionWithTarget:(CCNode *)followedNode strength:(float)strength distance:(float)distance;

@end

TTSoftFollow.m

#import "TTSoftFollow.h"
#import "CGPointExtension.h"

@interface TTSoftFollow ()

@property (nonatomic, retain) CCNode  *followedNode;
@property (nonatomic, assign) CGFloat strength;
@property (nonatomic, assign) CGFloat distance;

@end

@implementation TTSoftFollow

+ (id)actionWithTarget:(CCNode *)followedNode
{
    return [self actionWithTarget:followedNode strength:1.0f];
}

+ (id)actionWithTarget:(CCNode *)followedNode strength:(float)strength
{
    return [self actionWithTarget:followedNode strength:strength distance:0.0f];
}

+ (id)actionWithTarget:(CCNode *)followedNode strength:(float)strength distance:(float)distance
{
    return [[[self alloc] initWithTarget:followedNode strength:strength distance:distance] autorelease];
}

- (id)initWithTarget:(CCNode *)followedNode strength:(float)strength distance:(float)distance
{
    if (self = [super init]) {
        _followedNode   = [followedNode retain];
        _strength       = strength;
        _distance       = distance;
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone
{
    CCAction *copy = [[[self class] allocWithZone:zone] init];
    copy.tag = _tag;
    return copy;
}

- (void)dealloc
{
    [_followedNode release];
    [super dealloc];
}

- (void)step:(ccTime)dt
{
    float distX = self.followedNode.position.x - [_target position].x;
    float distY = self.followedNode.position.y - [_target position].y;
    kmVec2 nodeToTargetVec = (kmVec2){distX,distY};
    
    if (kmVec2Length(&nodeToTargetVec) > _distance) {
        // ベクトルをdistanceの大きさまで縮める
        kmVec2 goalToTargetVec;
        kmVec2Scale(&goalToTargetVec, &nodeToTargetVec, _distance/kmVec2Length(&nodeToTargetVec));
        
        // nodeからgoalまでのベクトル
        kmVec2 nodeToGoalVec;
        kmVec2Subtract(&nodeToGoalVec, &nodeToTargetVec, &goalToTargetVec);
        
        // strengthを乗算
        kmVec2 resultVec;
        kmVec2Scale(&resultVec, &nodeToGoalVec, min(_strength,1));
        
        // 単位ベクトル分だけ必ず進むようにする
        if (kmVec2Length(&resultVec) < 1) {
            kmVec2Normalize(&resultVec, &resultVec);
        }
        
        [_target setPosition:ccpAdd([_target position], ccp(resultVec.x, resultVec.y))];
    }
}

- (BOOL)isDone
{
    return !self.followedNode.isRunning;
}

@end

CCFollowとほとんど同じようなコードです。
followedNodeが先にdeallocされたら、isDoneのreturnがYESになるのでstopが呼ばれる。

よく理解せずに使っているblocksよりこっちを使おう。blocksは勉強しよう。

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

InsertSubviewしたときはaddSubviewはいらない

UIViewをサブビューとして画面に表示させたいとき、

UIView *subview = [[[UIView alloc] init] autorelease];
[parentView addSubview:subview];

のように書くとparentViewのサブビューリストの末尾にsubviewが追加される(一番上に表示される)

で、既存のサブビューの並び順を変えたいときは、

[parentView bringSubviewToFront:subview];  // subviewのインデックスを末尾に移動する(一番上に表示される)
[parentView sendSubviewToBack:subview];   // subviewのインデックスを先頭に移動する(一番後ろに表示される)

これらを使う。

InsertSubviewはサブビューリストに新たにサブビューを挿入する

勘違いしていたのだけど、insertSubviewも前述2つのメソッドと同じように既存サブビューのインデックスをいじるものだと思っていた。
考えてみればinsertって付いているのだから、気がつけよと…

[parentView insertSubview:subview1 aboveSubview:subview2];  // 既存subview2の上に新たにsubview1が挿入される
[parentView insertSubview:subview1 belowSubview:subview2];  // 既存subview2の下に新たにsubview1が挿入される
[parentView insertSubview:subview1 atIndex:index];                  // parentViewのサブビューリストのindexの位置に新たにsubview1を挿入する

新たに追加するので、insertSubviewを使用するならaddSubviewしてはいけない(※追記)

[parentView insertSubview:subview atIndex:[parentView.subviews count]];

と、

[parentView addSubview:subview];

は同義ってことですね。

今まではaddSubviewした後にinsertSubviewで順番を入れ替えるとかアホなことしてました。

※追記 2013/05/09 10:16

既にsubviewとなっているviewをもう一度addSubviewしようとしても特に問題なかった。
その場合貼り直しになるので、indexは最大になる。
既にsuperviewを持っているviewを他のviewにaddSubviewした場合の振る舞い - taoru's memo

kmVec2でのベクトル計算

Kazmath - Kazade/kazmath

cocos2dにも標準で入っているライブラリKazmathの勉強をする。

今回は特にkmVec2に関するメソッドを色々と試す。
以前にも一度ベクトル計算を試している(※)が、kmVec2Dotとか何をしているか分かっていない。
2Dでベクトル計算ができると色々と捗りそうだったので、この機会にメソッドの効果をメモしておく。

※ベクトル計算使ってる記事
CCNodeのカテゴリで、他のCCNodeをゆったり追うSoftFollowというのを作ってみた その2 - 追記2/26 - taoru's memo

kmVec2メソッド
length(kmVec2) x,yの二乗を足した値の平方根を返す(大きさ)
lengthSq(kmVec2) x,yの二乗を足した値を返す
normalize(kmVec2&) 単位ベクトルを返す
add(kmVec2, kmVec2) 2ベクトルの加算結果を返す
sub(kmVec2, kmVec2) 2ベクトルの減算結果を返す
dot(kmVec2, kmVec2) 2ベクトルの内積を返す
scale(kmVec2&, kmScalar) 指定ベクトルをkmScalar倍する
equal(kmVec2, kmVec2) 2ベクトルが等しければtrueを返す
transform(kmVec2, kmMat3) 指定ベクトルを行列変換する

計算後のベクトルを表示したかったので、適当に表示用のメソッドをまず作った

- (void)displaySegmentWithVec:(kmVec2)vec color:(ccColor3B)color
{
    CCDrawNode *drawNode   = (CCDrawNode *)[self getChildByTag:0];
    if (!drawNode) {
        drawNode = [CCDrawNode node];
        drawNode.position = ccp(self.contentSize.width*0.5f, self.contentSize.height*0.5f);
        [self addChild:drawNode z:0 tag:0];
    }

    // fixedVecにvecを50倍したベクトルを代入
    kmVec2 fixedVec;
    kmVec2Scale(&fixedVec, &vec, 50.0f);
    CGPoint vecPoint = ccp(fixedVec.x, fixedVec.y);
    
    // (0,0)から(vec.x,vec.y)までの線分を描画
    [drawNode drawSegmentFrom:CGPointZero to:vecPoint radius:1.5f color:ccc4FFromccc3B(color)];

    // 元のVectorを表示
    CCLabelTTF *pointLabel = [CCLabelTTF labelWithString:@"" fontName:@"Helvetica" fontSize:16];
    pointLabel.position = ccpAdd(drawNode.position, ccp(vecPoint.x, vecPoint.y + 10));
    pointLabel.string   = NSStringFromCGPoint(ccp(vec.x, vec.y));
    pointLabel.color    = color;
    [self addChild:pointLabel z:1];
}

では補完が出る順(名前順)に試していく。

kmVec2Add

kmVec2* kmVec2Add(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2)
{
	pOut->x = pV1->x + pV2->x;
	pOut->y = pV1->y + pV2->y;

	return pOut;
}

2ベクトルの加算

        kmVec2 vec1  = (kmVec2){1, 1};
        kmVec2 vec2  = (kmVec2){2, 0};
        
        kmVec2 resultVec;
        
        kmVec2Add(&resultVec, &vec1, &vec2);
        
        [self displaySegmentWithVec:vec1 color:ccGREEN];
        [self displaySegmentWithVec:vec2 color:ccRED];
        [self displaySegmentWithVec:resultVec color:ccWHITE];

白が計算結果(resultVec)

kmVec2AreEqual

int kmVec2AreEqual(const kmVec2* p1, const kmVec2* p2)
{
	return (
				(p1->x < p2->x + kmEpsilon && p1->x > p2->x - kmEpsilon) &&
				(p1->y < p2->y + kmEpsilon && p1->y > p2->y - kmEpsilon)
			);
}

ベクトルの比較( are なのは複数形ってこと?)

        kmVec2 vec1  = (kmVec2){1, 1};
        kmVec2 vec2  = (kmVec2){1, 1};
        BOOL isEqualVec = kmVec2AreEqual(&vec1, &vec2);
        CCLOG(@"isEqualVec = %d",isEqualVec); // 1 (YES) が出力される

kmVec2Dot

kmScalar kmVec2Dot(const kmVec2* pV1, const kmVec2* pV2)
{
    return pV1->x * pV2->x + pV1->y * pV2->y;
}

ベクトルの内積値を計算して返す。
"ドット"ってなんだよって思ってたけど、ドット積(点乗積、内積)のことらしい。
(wikipedia)ドット積
内積を表す式が[A・B]だからか…
内積は計算にcosθが含まれているので、2ベクトルのなす角を得たり直行判定に使える。

        kmVec2 vec1  = (kmVec2){1, 1};
        kmVec2 vec2  = (kmVec2){0, 2};
        
        float vecDot = kmVec2Dot(&vec1, &vec2);
        CCLOG(@"vec1とvec2の内積 = %f",vecDot);  // 2.0が出力される

kmVec2Fill

kmVec2* kmVec2Fill(kmVec2* pOut, kmScalar x, kmScalar y)
{
    pOut->x = x;
    pOut->y = y;
    return pOut;
}

ベクトルの代入. ただの代入なのでコード割愛.

kmVec2Length

kmScalar kmVec2Length(const kmVec2* pIn)
{
    return sqrtf(kmSQR(pIn->x) + kmSQR(pIn->y));
}

ベクトルの大きさを返す.
kmVec2LengthSqというメソッドもあるが、そちらは sqrtfする前の値を返す

kmVec2Normalize

kmVec2* kmVec2Normalize(kmVec2* pOut, const kmVec2* pIn)
{
	kmScalar l = 1.0f / kmVec2Length(pIn);

	kmVec2 v;
	v.x = pIn->x * l;
	v.y = pIn->y * l;

	pOut->x = v.x;
	pOut->y = v.y;

	return pOut;
}

大きさ1の単位ベクトルを計算して返す。
もっと早く知っておくべきだった…orz

        kmVec2 vec1  = (kmVec2){3, 2};

        kmVec2 resultVec;
        
        kmVec2Normalize(&resultVec, &vec1);
        CCLOG(@"resultVecの大きさ = %f", kmVec2Length(&resultVec)); // 1.0 が出力される
        
        [self displaySegmentWithVec:vec1 color:ccGREEN];
        [self displaySegmentWithVec:resultVec color:ccWHITE];

kmVec2Scale

kmVec2* kmVec2Scale(kmVec2* pOut, const kmVec2* pIn, const kmScalar s)
{
	pOut->x = pIn->x * s;
	pOut->y = pIn->y * s;

	return pOut;
}

ベクトルの大きさを指定倍する.

        kmVec2 vec1  = (kmVec2){1, 1};

        kmVec2 resultVec;
        
        kmVec2Scale(&resultVec, &vec1, 2.0f);
        CCLOG(@"resultVecの大きさ = %f", kmVec2Length(&resultVec)); // 2.828427 が出力される
        
        [self displaySegmentWithVec:vec1 color:ccGREEN];
        [self displaySegmentWithVec:resultVec color:ccWHITE];

kmVec2Subtract

kmVec2* kmVec2Subtract(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2)
{
	pOut->x = pV1->x - pV2->x;
	pOut->y = pV1->y - pV2->y;

	return pOut;
}

2ベクトルの減算結果ベクトルを返す

        kmVec2 vec1  = (kmVec2){1, 2};
        kmVec2 vec2  = (kmVec2){3, 1};

        kmVec2 resultVec;
        
        kmVec2Subtract(&resultVec, &vec1, &vec2);
        
        [self displaySegmentWithVec:vec1 color:ccGREEN];
        [self displaySegmentWithVec:vec2 color:ccRED];
        [self displaySegmentWithVec:resultVec color:ccWHITE];

緑ベクトル − 赤ベクトル = 白ベクトル

kmVec2Transform

kmVec2* kmVec2Transform(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM)
{
    kmVec2 v;

    v.x = pV->x * pM->mat[0] + pV->y * pM->mat[3] + pM->mat[6];
    v.y = pV->x * pM->mat[1] + pV->y * pM->mat[4] + pM->mat[7];

    pOut->x = v.x;
    pOut->y = v.y;

    return pOut;
}

ベクトルを行列で一次変換する.
数Bを真面目に勉強していなかったので辛い。
1時変換の参考にしたページ:■行列と1次変換

kmMat3は三次行列だけど、kmVec2は二次元なので2行3列しか使われていない(mat[2],mat[5],mat[8]を使ってない)

        kmVec2 vec1  = (kmVec2){1, 2};

        kmVec2 resultVec;
        
        // Y軸に関する対称移動
        kmMat3 mat3 = (kmMat3){
            -1, 0, 0,
             0, 1, 0,
             0, 0, 0
        };
        kmVec2Transform(&resultVec, &vec1, &mat3);
        
        [self displaySegmentWithVec:vec1 color:ccGREEN];
        [self displaySegmentWithVec:resultVec color:ccWHITE];

行列の知識がぶっ飛んでたので、とりあえず対称移動だけ頑張って思い出しました。

kmVec2TransformCoord

kmVec2* kmVec2TransformCoord(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM)
{
    assert(0);
    return NULL;
}

使うとAsserionを吐いて落ちる。
訳がわからないよ。

kmVec3TransformCoordは実装されていたのでそっちを覗いてみたところ、
どうやらkmVec4Transform(1次元上の行列変換)にかけて、結果の4次元目で結果のxyzを割っている(?)

仮にkmVec3TransformCoordと同じようなロジックでkmVec2TransformCoordを実装するとしたら、
まずkazmath/vec3.h をincludeしてからこんな感じになるのだが…

kmVec2* kmVec2TransformCoord(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM)
{
    kmVec3 v;
    kmVec3 inV;
    kmVec3Fill(&inV, pV->x, pV->y, 1.0);
    
    kmVec3Transform(&v, &inV,pM);
    
    pOut->x = v.x / v.z;
    pOut->y = v.y / v.z;
    
    return pOut;
}

kmVec3Transformの3番目の引数はkmMat4のため、このコードは動かない。
kmVec4Transformの3番目の引数はkmMat4のため、kmVec3TransformCoordは動く。

行列変換は難しい(小並感)

以上で kmVec2 を関するメソッドを全て試してみたことになる。
結構使えるかも?

参考ページ
平面幾何におけるベクトル演算 3.内積と外積
行列と一次変換

2ベクトルのなす角を求める

なす角を求めるには、arcCos( 内積 / (vec1の大きさ * vec2の大きさ) ) を計算すればよい

        kmVec2 vec1  = (kmVec2){1, 1};
        kmVec2 vec2  = (kmVec2){2, 0};

        // 内積
        float vecDot = kmVec2Dot(&vec1, &vec2);
        
        float vec1Length = kmVec2Length(&vec1);
        float vec2Length = kmVec2Length(&vec2);
        
        float radian = acosf(vecDot / (vec1Length * vec2Length));
        CCLOG(@"vec1 と vec2 のなす角 = %f",kmRadiansToDegrees(radian));
        
        [self displaySegmentWithVec:vec1 color:ccGREEN];
        [self displaySegmentWithVec:vec2 color:ccRED];

"vec1 と vec2 のなす角 = 45.0 が出力される

cocos2dv2.1で追加されたCCClippingNodeを試す

この記事は、cocos2dv2.1で追加されたCCDrawNodeを試す - taoru's memo の続きです。
今回はCCClippingNodeを試す。これを使うといわゆるmask効果を得られる。

が、適当に試しててもなかなか思い通りにできなかったので、練習する。

CCClippingNode.h についてるコメント

/** CCClippingNode is a subclass of CCNode.
It draws its content (childs) clipped using a stencil.
The stencil is an other CCNode that will not be drawn.
The clipping is done using the alpha part of the stencil (adjusted with an alphaThreshold).
*/

stencilによって、clippingNodeにaddChildされたCCNodeをクリッピングする。
そのstencilは(clippingNodeとは別の?)CCNodeで、描画されることがない。
stencilの透過部分がクリッピングに使われる

setできるparameter

/** The CCNode to use as a stencil to do the clipping.
 The stencil node will be retained.
 This default to nil.
 */
@property (nonatomic, retain) CCNode *stencil;

/** The alpha threshold.
 The content is drawn only where the stencil have pixel with alpha greater than the alphaThreshold.
 Should be a float between 0 and 1.
 This default to 1 (so alpha test is disabled).
 */
@property (nonatomic) GLfloat alphaThreshold;

/** Inverted. If this is set to YES,
 the stencil is inverted, so the content is drawn where the stencil is NOT drawn.
 This default to NO.
 */
@property (nonatomic) BOOL inverted;

以下は私の解釈

  • stencil
    • stencilTestに使用されるCCNode, このNodeによってマスクされる
  • alphaThreshold
    • 直訳でalpha値のしきい値
    • stencilのうちalphaThresholdより大きいalpha値を持つpixelのみコンテンツが描画される。
  • inverted
    • これがYESだとstencilTestの結果が逆転して、stencilが描画されない領域だけが描画されるようになる。

英語が弱すぎてかなりあやふやですが、そこはTry&Errorでカバーしましょう…


が、いっこうにうまくいかない

まずAppDelegate.m のCCGLViewを定義している箇所を書き換えて、stencil bufferを有効にする(たぶん)

	// Create an CCGLView with a RGB565 color buffer, and a depth buffer of 0-bits
	CCGLView *glView = [CCGLView viewWithFrame:[window_ bounds]
								   pixelFormat:kEAGLColorFormatRGB565
								   depthFormat:GL_DEPTH24_STENCIL8_OES //GL_DEPTH_COMPONENT24_OES
							preserveBackbuffer:NO
									sharegroup:nil
								 multiSampling:NO
							   numberOfSamples:0];

サンプルを元に、ClippingNodeを試す(NGコード)

- (id)init
{
    if (self = [super init]) {
        CGPoint square[]   = { {-100,-100}, {100,-100}, {100,100}, {-100,100} };
        CGPoint triangle[] = { {-50,-50}, {50,-50}, {0,50}};
        ccColor4F green = ccc4FFromccc3B(ccGREEN);
        ccColor4F red  = ccc4FFromccc3B(ccRED);
        
        // contents部分 これを表示させたい
        CCDrawNode *content = [CCDrawNode node];
        [content drawPolyWithVerts:square count:4 fillColor:green borderWidth:0 borderColor:green];
        content.position = ccp(50, 50);
        
        // stencil これでマスクする
        CCDrawNode *stencil = [CCDrawNode node];
        [stencil drawPolyWithVerts:triangle count:3 fillColor:red borderWidth:0 borderColor:red];
        stencil.position  = ccp(50, 50);
        [stencil runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:1 angle:90]]];

        CCClippingNode *clippingNode = [CCClippingNode clippingNode];
        clippingNode.anchorPoint          = ccp(0.5f, 0.5f);
        clippingNode.alphaThreshold    = 0.05f;
        clippingNode.stencil = stencil;

        // clippingしたいnodeを、clippingNodeにaddChildする
        [clippingNode addChild:content];
        
        clippingNode.position = ccp(self.contentSize.width/2 -50, self.contentSize.height/2 - 50);
        [self addChild:clippingNode];
        
    }
    return self;
}

これを実行すると…

サンプルとほぼ同じなのに、できない。何がいけないんだろう。

追記1

alphaThresholdの指定を外したらできた!(defaultでは1になってる)
alphaThresholdについての理解ができていない。まだまだマスターには遠いな。

上記からalphaThresholdを外して、contentにCCSpriteを使ってみた。

- (id)init
{
    if (self = [super init]) {
        
        CGPoint triangle[] = { {-100,-100}, {100,-100}, {0,100}};
        ccColor4F red   = ccc4FFromccc3B(ccRED);
        
        // contents部分 これを表示させたい
        CCSprite *content = [CCSprite spriteWithFile:@"Default@2x.png"];
        content.position = ccp(50, 50);
        
        // stencil これでマスクする
        CCDrawNode *stencil = [CCDrawNode node];
        [stencil drawPolyWithVerts:triangle count:3 fillColor:red borderWidth:0 borderColor:red];
        stencil.position    = ccp(50, 50);
        [stencil runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:1 angle:90]]];

        CCClippingNode *clippingNode = [CCClippingNode clippingNode];
        clippingNode.anchorPoint     = ccp(0.5f, 0.5f);
        clippingNode.stencil         = stencil;

        [clippingNode addChild:content];
        
        clippingNode.position = ccp(self.contentSize.width/2 -50, self.contentSize.height/2 - 50);
        [self addChild:clippingNode];
    }
    return self;
}

stencilを回転させている。

clippingNode.inverted = YES; にすると描画範囲が逆転

こいつは面白い。

次にstencil部分に CCSpriteを使ってみた。

ペイントソフトで作ったお粗末な画像

- (id)init
{
    if (self = [super init]) {
        
//        CGPoint triangle[] = { {-100,-100}, {100,-100}, {0,100}};
//        ccColor4F red   = ccc4FFromccc3B(ccRED);
        
        // contents部分 これを表示させたい
        CCSprite *content = [CCSprite spriteWithFile:@"Default@2x.png"];
        content.position = ccp(50, 50);
        
        // stencil これでマスクする
//        CCDrawNode *stencil = [CCDrawNode node];
//        [stencil drawPolyWithVerts:triangle count:3 fillColor:red borderWidth:0 borderColor:red];
        CCSprite *stencil = [CCSprite spriteWithFile:@"star.png"];
        stencil.position    = ccp(50, 50);
        stencil.scale       = 1.5f;
        [stencil runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:1 angle:90]]];

        CCClippingNode *clippingNode = [CCClippingNode clippingNode];
        clippingNode.anchorPoint     = ccp(0.5f, 0.5f);
        clippingNode.stencil         = stencil;
        clippingNode.alphaThreshold  = 0.01;
        
        [clippingNode addChild:content];
        
        clippingNode.position = ccp(self.contentSize.width/2 -50, self.contentSize.height/2 - 50);
        [self addChild:clippingNode];
    }
    return self;
}

結果

CCSpriteをstencilに用いるときは、alphaThresholdを設定する必要があった。
default(1.0f)だと何も表示されなかったので、0より大きい0.01をセットした。


    • 2013/03/06

childやstencilにCCNodeを使い、更にactionがつく場合の制御が意外と難しい。
CCNodeのcontentSizeが関係しているかも?

cocos2dv2.1で追加されたCCDrawNodeを試す

cocos2d v2.1-rc0 が出ましたので、色々試してみようかと。

中でも以下の2つ

  • CCClippingNode
  • CCDrawNode

をマスターしたいところです。

とりあえずCCDrawNodeを練習した記録を残す。

まずはサンプルを見ながらシンプルな三角形を書くコード

- (id)init
{
    if (self = [super init])    {
        
        CCDrawNode *drawNode = [CCDrawNode node];
        
        CGPoint triangleVerts[] = {{-100,-100}, {100,-100}, {0, 100}};
        ccColor4F red           = {1, 0, 0, 1};
        ccColor4F green         = {0, 1, 0, 1};
        
        [drawNode drawPolyWithVerts:triangleVerts count:3 fillColor:red borderWidth:2 borderColor:green];
        
        drawNode.position = ccp(self.contentSize.width/2, self.contentSize.height/2);
        [self addChild:drawNode];

    }
    return self;
}

borderも角が尖ってる。borderWidth:0でフチ無し。

drawPolyやらdrawDotを連続で呼び出すと、合成したdrawNodeになってくれる.

- (id)init
{
    if (self = [super init])    {
        
        CCDrawNode *drawNode = [CCDrawNode node];
        
        CGPoint triangle[] = {{-100,-100}, {100,-100}, {0, 100}};
        CGPoint square[] = {{-60,-60}, {60,-60}, {60,60}, {-60, 60}};
        
        ccColor4F red = {1, 0, 0, 1};
        ccColor4F green = {0, 1, 0, 1};
        ccColor4F blue = {0, 0, 1, 1};
        ccColor4F yellow = ccc4FFromccc3B(ccYELLOW); // covertマクロも用意されている
        
        // 三角形の描画
        [drawNode drawPolyWithVerts:triangle count:3 fillColor:red borderWidth:2 borderColor:green];
        
        // 四角形の描画
        [drawNode drawPolyWithVerts:square   count:4 fillColor:green borderWidth:2 borderColor:blue];
        
        // 線の描画
        [drawNode drawSegmentFrom:ccp(-50, -50) to:ccp(50, 50) radius:5.0f color:yellow];
        
        // 点の描画
        [drawNode drawDot:ccp(0, 0) radius:10.0f color:red];
        
        drawNode.position = ccp(self.contentSize.width/2, self.contentSize.height/2);
        [self addChild:drawNode];

    }
    return self;
}

星形を描画してみようと思ったけど、どうもうまくいかない。
凹みのある多角形を綺麗に表示されるためのコツが知りたい。

CCDrawNode *drawNode = [CCDrawNode node];

CGPoint starVerts[] = {
    {-100,-100}, {0, -50},
    {100, -100}, {50,-15},
    {100,25}, {35,25},
    {0, 100}, {-35,25},
    {-100,25}, {-50,-15}
};

ccColor4F red = {1, 0, 0, 1};
ccColor4F yellow = ccc4FFromccc3B(ccYELLOW);

[drawNode drawPolyWithVerts:starVerts count:10 fillColor:yellow borderWidth:2 borderColor:red];

drawNode.position = ccp(self.contentSize.width/2, self.contentSize.height/2);
[self addChild:drawNode];


starVerts[]に始点と同位置の{-100,100}を加えて、count:11にして試してもダメだった。

内部的には、いくつかの三角形に分割してポリゴンを描画しているようだから、
工夫してやらないとうまくいかないかも。
複雑な図形はpng用意してspriteWithFileした方がよっぽど簡単ですね(当たり前)

QLOOKアクセス解析