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

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

【SpriteKit】Sprite Kit vs cocos2d パフォーマンス対決! 意外にも?

次のプロジェクトでSprite Kitにチャレンジしようかcocos2dを引き続き使おうか迷っています。
そこで、パフォーマンス対決をやってみました。

ちなみにSprite Kitのプロジェクトを作るのは簡単で、Xcode5のWelcomeスクリーンからCreate a new Xcode projectをクリックし、テンプレート選択画面でiOSのApplicationからSpriteKit Gameを選ぶだけです。

このテンプレートプロジェクトを実行すると、お決まりのHello, World!が表示され、画面をタップする毎にクルクルと回る宇宙船が表示されます。
今回は、これと同じことをするプログラムをcocos2dでも作成し、60fpsを維持できなくなるときの宇宙船の数を比較する事で性能を比較してみたいと思います。
比較にはiPhone4Sを使用します。
ただ、謎な事にSprite Kitのテンプレートプロジェクトでは宇宙船が何故か縦横2倍の大きさで表示されてしまうようです。Spaceship.pngの画像サイズは394x397ピクセルなのですが、普通に実行すると次のようにほぼ画面いっぱいに拡大されて表示されてしまいます。
f:id:takujidev:20131020135605j:plain:w300
これはイマイチなの縦横のスケールを0.5倍にしてcocos2dと同じ大きさになるようにプログラムを変更しました。
また、rotateのアクションに正の値(ちなみにSprite Kitはラジアン、cocos2dは度)を与えたときのスプライトの回転方向がcocos2dでは時計回り、Sprite Kitでは反時計回りになりますが、これはパフォーマンスには無関係なのでそのままにしてあります。

こちらの紺色の背景がSprite Kitです。Hello, World!に1ノード使っているので、表示のノード数は宇宙船の数+1になります。
f:id:takujidev:20131020141335j:plain:w300

こちらの黒の背景はcocos2dです。こちらも同様にHello, World!表示に1ノード使っています。
f:id:takujidev:20131020140711j:plain:w300


どちらもたまに一瞬フレームレートが落ちる事がありますがそれは無視して60fpsを明らかに維持できなくなったときを終了とします。

さて、結果はどうなるか?

まずはSprite Kitから。
f:id:takujidev:20131020141650j:plain:w300

59ノード。
という事は宇宙船58機で60fpsの維持が不可能になるということです。

そしてcocos2d。
f:id:takujidev:20131020141843j:plain:w300

あらっ?66ノード。
つまり宇宙船65機で60fpsの維持が不可能になりました。

したがいまして、cocos2dの勝ちです。(ちょっと待てい!という方、後日談ありです。当日ですが。)

予想外の結果となりましたが、その差がそれほど大きくないのがSprite Kitにとっては救いです。

また、今回のテストではcocos2dのスプライトバッチノードを使っていません。
かなり前の「【cocos2d】CCSpriteBatchNodeがどれほど速いか試してみる」の実験ではスプライトバッチノードを使う事で約5倍の高速化ができました。今回のプログラムではスプライトを回転させるアクションが入っているのでそこまでの高速化はできないと思いますが、今度試してみたいと思います。
Sprite Kitの方はよくわかりませんが、恐らくバッチ処理はしていないと思いますので、もう少しいろいろ試してみたいと思います。

今回の実験で使用したプログラムのメイン部分は次のようになっています。

Sprite Kit

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        
        self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
        
        SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
        
        myLabel.text = @"Hello, World!";
        myLabel.fontSize = 30;
        myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
                                       CGRectGetMidY(self.frame));
        
        [self addChild:myLabel];
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    
    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        
        SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];
        
        sprite.position = location;
        
        sprite.xScale = 0.5; //何故か2倍サイズで表示されるので修正
        sprite.yScale = 0.5; //同上
        
        SKAction *action = [SKAction rotateByAngle:M_PI duration:1];
        
        [sprite runAction:[SKAction repeatActionForever:action]];
        
        [self addChild:sprite];
    }
}

cocos2d

-(id) init
{
	// always call "super" init
	// Apple recommends to re-assign "self" with the "super's" return value
	if( (self=[super init]) ) {
        
        
		
		// create and initialize a Label
		CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello, World!" fontName:@"Chalkduster" fontSize:30];

		// ask director for the window size
		CGSize size = [[CCDirector sharedDirector] winSize];
	
		// position the label on the center of the screen
		label.position =  ccp( size.width /2 , size.height/2 );
		
		// add the label as a child to this Layer
		[self addChild: label];
        
        self.isTouchEnabled = YES;
        
	}
	return self;
}

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInView:touch.view];
        location = [[CCDirector sharedDirector] convertToGL:location];
        
        CCSprite *sprite = [CCSprite spriteWithFile:@"Spaceship.png"];
        
        sprite.position = location;
        
        id action = [CCRotateBy actionWithDuration:1 angle:180];
        
        [sprite runAction:[CCRepeatForever actionWithAction:action]];
        
        [self addChild:sprite];
    }

    
}

当日の後日談

私、「ハッ」と気がつきました。もしかしたらSprite Kitのスプライトの大きさの問題はファイル名に@2xをつければ解決するのではないかと。

早速プロジェクトのファイル名をSpaceship@2x.pngに変更し、上記のxscale, yscaleの部分をコメントアウトして実行して見ました。

その結果は...
f:id:takujidev:20131020145821j:plain:w300

68ノード!

やりました。Sprite Kitが逆転しました!
宇宙船67機で60fpsの維持が不可能になりましたので、
Sprite Kitがcocos2dより宇宙船2機分速いです!

結論。
Retina機で実行する場合ファイル名には忘れずに@2xをつけましょう。

続編「Sprite Kit vs cocos2d パフォーマンス対決 (続編) バッチノードでばっちり?」もどうぞご覧下さい。
https://itunes.apple.com/jp/app/travelshooting-jp-toraberushutingu/id917570972?mt=8&uo=4&at=10laCt
https://itunes.apple.com/jp/app/beecluster-wu-liaono-zongsukurorushutingugemu/id663801586?mt=8&uo=4&at=10laCt