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でもできそう。
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でのベクトル計算
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];
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の単位ベクトルを計算して返す。
もっと早く知っておくべきだった…orzkmVec2 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];
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部分に 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; }
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];
内部的には、いくつかの三角形に分割してポリゴンを描画しているようだから、
工夫してやらないとうまくいかないかも。
複雑な図形はpng用意してspriteWithFileした方がよっぽど簡単ですね(当たり前)