夏までにiPhone アプリつくってみっか!

趣味でiPhone/Androidアプリを開発し、日々勉強した事を書いています。オープンワールド系レースゲームをUnityで開発中です。

【cocos2d】CCSpriteBatchNodeがどれほど速いか試してみる

さて、前回のポストで書いたとおり、いろいろな問題が発生しましたが、無事にcocos2dとARCを一緒に使う事ができるようになりました。

プロトタイピングと呼べるほどのものではありませんが、簡単なプログラムを作ってまずはObjective-Cとcocos2dに体を慣らしたいと思います。

最初はiPhoneに同じキャラクターをたくさん表示する事から始めたいと思います。そして、先日のポストで書いたCCSpriteBatchNodeを使う事でどれだけ速度向上が望めるのか検証してみます。

で、作ったのがこのプログラム。まずは、cocos2DのテンプレートのHelloWorldLayerから不要な部分を取り除きます。そして、CCSpriteBatchNodeを使う場合、使わない場合の比較ができるように#ifdefでコンパイル時に切り替えられるようにします。スプライトバッチノードで指定するテクスチャはプロジェクトのResourcesフォルダーに入っていたIcon-72.pngを使いました。

@implementation HelloWorldLayer

+(CCScene *) scene
{
	CCScene *scene = [CCScene node];
	HelloWorldLayer *layer = [HelloWorldLayer node];
	[scene addChild: layer];
	return scene;
}

-(id) init
{
#define NumberOfShip 10000
#define Batch
    if( (self=[super init]) ) {
#ifdef Batch
        CCSpriteBatchNode* batch = [CCSpriteBatchNode batchNodeWithFile:@"Icon-72.png"];
        [self addChild:batch];
#endif
        for (int i = 0; i < NumberOfShip; i++) {
            MySprite* myShip = [[MySprite alloc] initWithImage];
#ifdef Batch
            [batch addChild:myShip];
#else
            [self addChild:myShip];
#endif
        }   
    }
    return self;
}

@end

NumberOfShipの設定を返ることで表示するキャラクターの数を帰られます。#define Batchをコメントアウトするとバッチノードを使わずにスプライトを表示します。

そして、キャラクター表示用にCCSpriteを継承して新たにクラスを作ります。CCSpriteを継承するのはあまり良くないそうですが、バッチノードにaddChildできるのはCCSpriteかそのサブクラスのインスタンスだけなので、とりあえずCCSpriteのサブクラスとしてMySpriteクラスを作成しました。initWithImageではとりあえず画面の真ん中にスプライトを表示し、updateメソッドで毎フレームランダムな位置に移動します。スプライトに表示する画像は先ほどスプライトバッチノードに指定したファイルと同じ物を使います。そうしないとスプライトバッチノードを使う意味がないです。

ヘッダファイルは

#import <Foundation/Foundation.h>
#import "cocos2d.h"

@interface MySprite : CCSprite {
    
}

-(id)initWithImage;

@end

実装ファイルは

#import "MySprite.h"

@implementation MySprite

- (id)initWithImage
{
    if ((self = [super initWithFile:@"Icon-72.png"])) {
        CGSize screen = [CCDirector sharedDirector].winSize;
        CGPoint myPosition;
        myPosition.x = screen.width / 2;
        myPosition.y = screen.height / 2;
        self.position = myPosition;
        
        [self scheduleUpdate];
    }
    return self;
}

- (void)update:(ccTime)delta
{
    CGSize screen = [CCDirector sharedDirector].winSize;
    CGPoint myPosition;
    myPosition.x = CCRANDOM_0_1() * screen.width;
    myPosition.y = CCRANDOM_0_1() * screen.height;
    self.position = myPosition;
}
@end

HelloWorldLayerでMySpriteクラスのインスタンスをallocして、initWithImageメソッドで初期化します。ここでメソッド名をinitにすると無限ループに陥るとLearn cocos2d 2: Game Development for iOS
にはかかれているのでinitじゃないメソッド名にしてみました。
試しにinitにして動作を確認してみましたが普通に起動しました。でも念のため元に戻しておきましょう。

さて、それでは早速実行速度を調べてみます。

まずは、#define BatchをコメントアウトしてiPhoneシミュレーター(iOS5.1)で実行。
1イベントループの時間は1.03秒。
速いのか遅いのかよくわかりません。
次に#define Batchを生かして実行。
1イベントループの時間は1.00秒。
ほとんど同じです。
シミュレーター上ではスプライトバッチノードによる高速化の効能は得られないようです。
次にiPhone4S(iOS5.1)の実機で実行
まずはスプライトバッチノードなしの場合。
せっかくなのでスクリーンショットを貼りましょう。

f:id:takujidev:20130406233443j:plain

左下の3行の文字にご注目。1行目はフレーム毎のdrawのコール回数です。10000個のスプライトをそれぞれ別のdrawで処理しているのがわかります。
2行目は1フレームの処理にかかった時間。この場合約0.5秒です。
3行目はフレームレート。1フレーム0.5秒だとフレームレートは2fpsですが、60fpsと表示されています。あまりにもフレームあたりの処理時間が長くなると表示がおかしくなるのかもしれません。

次に、期待のCCSpriteBatchNodeの実力を見てみましょう。#define Batchを生かして、iPhone実機で実行します。
結果はこちら。

f:id:takujidev:20130406233444j:plain

drawコール数は1回、フレーム処理時間が約0.1秒、フレームレートは10fpsです。今回はフレームレートが正しく表示されました。

CCSpriteBatchNodeがどれほど速いか結果が出ました。約5倍の高速化です。
これは使わない手はないですね。
https://itunes.apple.com/jp/app/beecluster-wu-liaono-zongsukurorushutingugemu/id663801586?mt=8&uo=4&at=10laCt