【cocos2d】CCAnimationでスプライトをアニメーションしてみた
今回はキンバエのキャラクターを作りアニメーションをつけてみました。
まずはアニメーションに使用するハエの絵を描きます。
絵を描くのにはセルシスのClip Studio Paintというソフトを使っています。
ペンタブレットはWacomのBamboo Fun Mサイズです。完成したのがこれらの3枚の絵です。原寸の72x72ピクセルだと小さすぎるのでサイズは各辺4倍の288x288ピクセルで描きました。胴体と羽の部分はレイヤーを分けて合成しています。羽は閉じた状態の絵だけ描いて、開いた状態、中間の状態の絵は画像を閉じた状態の左右の羽を回転させて作りました。胴体と各羽を合成した状態でpngファイルとして書き出します。fly1.pngとfly3.pngは全く同じ絵ですがアニメーションの都合上ファイル名を分ける必要があるので別ファイルとします。
fly0.png
fly1.png
fly2.png
fly3.png
次に、これらの4枚の絵をTexturePackerで1枚のテクスチャアトラスにまとめます。やり方はこちらのポストをご参照ください。
こちらがテクスチャアトラスとしてまとめられた絵です。
ハエの絵が3枚しかない事にお気づきでしょうか?TexturePackerの"auto alias"というチェックボックスをチェックしておくと、同じ絵(この例の場合、中間の羽の位置の絵)は自動的に1つにまとめられ、メモリ使用量を節約してくれます。同じ絵が繰り返し現れるようなアニメーションでは非常に有効な機能だと思います。
TexturePackerで作成した.plistファイルと.pngファイルをプロジェクトにコピーしたら早速プログラム開始です。なお、fly0.pngなどの個別の絵のファイルはプロジェクトに加える必要はありません。
スプライトをアニメーションさせるコードはCCSpriteを継承して、当たり判定用の情報を追加したCollisionSpriteクラスに実装します。このメソッドでは受け取ったファイル名(の一部)に連番用の数字と拡張子を加え、スプライトフレームキャッシュからそのファイル名に対応するスプライトフレームを取り出します。
取り出したスプライトフレームは順にNSMutabuleArrayに追加され、そのArrayとパラメーターとして受け取ったアニメーションのフレーム間隔をanimationWithSpriteFrames:delay:メソッドに渡してCCAnimationオブジェクトを生成し、呼び出し元に戻します。
このルーチンはLearn cocos2d 2という本に書かれていたルーチンを使わせていただきました。
- (CCAnimation *)animationWithName:(NSString *)name frameCount:(int)count delay:(float)delay { NSMutableArray* frames = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count; i++) { NSString* file = [NSString stringWithFormat:@"%@%d.png", name, i]; CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:file]; [frames addObject:frame]; } return [CCAnimation animationWithSpriteFrames:frames delay:delay]; }
そして、先ほどのメソッドの呼び出し元が、こちらのやたらと引数が多くなってしまったinitWithPositionで始まるメソッドです。こちらは敵キャラクターのオブジェクトであるMyEnemyクラスに実装されています。MyEnemyクラスは前述のCollisionSpriteのサブクラスです。今回アニメーション用の情報として、name, AnimCount, delayの3個の引数を追加しました。
- (id)initWithPosition:(CGPoint)position frame:(CCSpriteFrame *)frame action:(CCAction *)action target:(CollisionSprite *)target maxAccel:(float)maxAccel maxSpeed:(float)maxSpeed brakeDistance:(float)brakeDistance name:(NSString *)name animCount:(int)count delay:(float)delay { if ((self = [super initWithTarget:target frame:frame maxAccel:maxAccel maxSpeed:maxSpeed brakeDistance:brakeDistance])) { self.radius = self.contentSize.width/2; self.tag = kEnemy; _target = target; [[MyParentNode sharedParentNode] addChild:_target]; [_target runAction:action]; CCAnimation* animation = [self animationWithName:name frameCount:count delay:delay]; id animate = [CCAnimate actionWithAnimation:animation]; id repeat = [CCRepeatForever actionWithAction:animate]; [self runAction:repeat]; } return self; }
アニメーションに関する部分は次の4行です。CCRepeatForeverのアクションを作り忘れると1回アニメーションして止まってしまいます。私はここでハマりました。
まず前述のメソッドでアニメーションのオブジェクトを作成
CCAnimation* animation = [self animationWithName:name frameCount:count delay:delay];
アニメーションのオブジェクトからアクションのオブジェクトを作成
id animate = [CCAnimate actionWithAnimation:animation];
そのアクションを繰り返すオブジェクトを作成
id repeat = [CCRepeatForever actionWithAction:animate];
繰り返すアクションを実行
[self runAction:repeat];
で、上のメソッドを呼び出すのがこちら。アニメーションに関係あるのは一番したの行の引数name, animCount, delayです。
nameにはNSStringオブジェクトのflyを渡します。この名前はテクスチャーに含まれているファイル名のベース部分です。animCountはアニメーションが何枚のフレームから成っているかの指定です。delayはフレーム間の間隔を秒数で指定します、
- (id)initFlyWithPosition:(CGPoint)position { CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"fly0.png"]; id moveBy = [CCMoveBy actionWithDuration:3.0 position:ccp(0, -500)]; CollisionSprite* target = [CollisionSprite node]; target.position = position; return [self initWithPosition:position frame:frame action:moveBy target:target maxAccel:60/60.0 maxSpeed:600/60.0 brakeDistance:100.0 name:@"fly" animCount:4 delay:0.03]; }
それでは、今回の実行画面の動画をご覧ください。
敵キャラクターの羽が高速に動いているのがわかりますか?