記事一覧

Cocos2d解説(5) CCSpriteBatchNode

■CCSpriteBatchNode

コマアニメーションを行う方法をWebで検索すると、CCSpriteBatchNode(旧CCSpriteSheet)を使う方法が紹介されているページにたどり着くことが多い。
確かに、後述のように有利な点も多いので利用価値はあるし推奨もされているのだが、利用するメリット、および利用の手順がなぜそうなるのか、を理解するには、「CCSpriteBatchNode の本来の目的はコマアニメーションではない」という点を知っていた方がいいだろう。

OpenGLにはバッチといわれる仕組みがあり、これを用いると、一回のOpenGL命令で同じ内容のテクスチャを大量に描画することができる(・・・らしい。公式ドキュメントにそう書いてあったので受け売りです; でも共通なテクスチャの描画処理をまとめれば、高速化できるというのはなんととなく分かる。)
で、同様の仕組みを cocos2d でも利用するために導入されたのが CCSpriteBatchNode である。

ところで、スプライトというのは、親ノード(CCLayerなどのCCNode系クラス)の child として登録し([parentNode addChild:aSprite]等)、親から描画依頼を受けるという使い方をする。
CCSpriteBatchNodeも、こうしたスプライトと同列に child として登録して使うのだが、描画依頼を受けても自分では描画せず、自身のchildに描画依頼を投げる。


[CCNode]─(描画依頼)┬→[CCSprite]
├→[CCSprite]
├→[CCSprite]
├→[CCSpriteBatchNode]─(描画依頼)┬→[CCSprite]
├→[CCSprite] ├→[CCSprite]
・ ├→[CCSprite]
・ ・
・ ・


もう少し具体的に言えば、 CCNode は1フレームごとに draw メソッドを実行するのだが、その際に各 child の draw を呼び出し、で、各 child がさらに child を持っていたらそれらの draw も呼び出すということだ。

このように描画依頼を child に投げるオブジェクトというのは、要は CCNode そのものであって(*1)、CCSpriteBatchNode は、それに加えてテクスチャ情報も持っているノードということになる。

CCSpriteBatchNode に child として登録したスプライトは、テクスチャが共通しているという前提のもとで描画処理を行えるので、特に数が多い場合 普通のCCNodeより高速に描画できる。

*1)なのでこんな回りくどい説明は本来必要ないのだが、自分が最初「CCSpriteBatchNodeは共通のテクスチャを共有するノードである」という説明を読んだときは何のことやらチンプンカンプンだったので、あえて説明してみた次第である。
ちなみに、以上の説明は、リファレンスでは次の一文で済まされている。
CCSpriteBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call (often known as "batch draw").


以上を踏まえると、こういうコードになるのも理解されるだろう。

//テクスチャの生成
CCTexture2d* texture = [[CCTexture2d alloc] initWithImage:@"texture.png"];

//バッチノードの作成・シーンへの追加
CCSpriteBatchNode *batch = [CCSpriteBatchNode
batchNodeWithTexture:texture];
[self addChild:batch];

//スプライトの作成・batchへの追加
sprite1 = [CCSprite spriteWithTexture:texture rect:rect1]; //バッチノードのテクスチャと同じでなければならない
[batch addChild:sprite1]; //batchNodeのchildとして追加。sprite1はbatchから描画依頼を受ける。
//異なるテクスチャが指定してあるとここでエラーとなる。

以下のように2個目・3個目も追加できる。もちろん、テクスチャは共通していなければならない。
この個数が増加してくるとバッチノードのメリットが生きてくるということになる。

sprite2 = [CCSprite spriteWithTexture:texture rect:rect2];
[batch addChild:sprite2];
sprite3 = [CCSprite spriteWithTexture:texture rect:rect3];
[batch addChild:sprite3];


このように、本来バッチノードはコマアニメーションやアトラス切り出しとは特に関係ない概念であり、CCSpriteBatchNodeを使うメリットは、あくまで大量のスプライトを扱う場合にパフォーマンスが良くなる点である。

したがって、バッチノードを作ってさらにそこにスプライトを追加、という風に一手間増えることを考えると、一度にそれほど多くのスプライトを使用しない、もしくはパフォーマンスを気にしないという場合無理して使う必要はないといえる。

逆に、アニメーションやアトラス切り出しが必要ない場面でも、同じスプライトを大量に使用する場合には積極的に使うべきだろう。

ちなみに、上記ではテクスチャが共通であることを分かりやすくするために CCTexture2d を明示的に作成しているが、

CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:@"texture.png"];

のようにすればテクスチャ作成(とテクスチャキャッシュへの登録)を自動的に行ってくれる。
この場合、batchに設定されているテクスチャは batch.textureAtlas で参照することができる。

また、CCSpriteBatchNode に 追加するスプライトの作り方として次のメソッドも用意されている。

sprite4 = [CCSprite spriteWithBatchNode:batch rect:rect4];

必ずバッチノードが持ってるテクスチャから切り出してスプライトを作るわけなのでこういうメソッドがあるのは親切なのだが、どうせならバッチノードに addChild までやってくれるともっと楽な気がするのは僕だけだろうか・・・。