読者です 読者をやめる 読者になる 読者になる

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

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

【Sprite Kit vs cocos2d】パフォーマンス対決(最終戦) どっちを使うか決めました!

cocos2d SpriteKit スペースハリアー iPhoneアプリ TravelShooting JP

さて、前回の対戦(前編後編)ではほぼ引き分けとなりましたSprite Kitとcocos2dのパフォーマンス対決、いよいよ今回が最終戦となります。
この対戦の勝者が私の(エセ)スペースハリアープロジェクトのゲームエンジンに採用されることになります。

今回は、プロジェクトの実現に不可欠なスペースハリアー的な地面の表示」におけるパフォーマンスを比較します。

次のような縞模様の地面を表示し、地平線の上下動と左右へのスクロールを行うプログラムを作ります。縦方向へのスクロールは現段階では実装しません(できてません)。

f:id:takujidev:20131108184310p:plain:w400
幅1600ピクセルx高さ640ピクセルのスプライトの頂点を移動させる事で地平線を上下させたり3D的に左右にスクロールさせます。この地面データの作り方につきましてはこちらこちらをご参照ください。

実際に動いている画面はこのようになります。
なお、これはcocos2dで実装したものですが、Sprite Kitでも全く同じ動作が実現できています。

Sprite Kit
Sprite KitではSKEffectNodeをつかってCIPerspectiveTransformというフィルターを掛けます。
地平線を上下させるには上の二つの頂点を上下に動かし、スクロールさせるには下の二つの頂点を左右に動かします。

それではパフォーマンスを見てみます。
負荷が高くなるように地平線を一番上まで上げた状態でチェックします。
画面上のfps表示では60fpsはキープできているようですが、Xcodeデバッグ画面で見てみるとGPUのフレーム時間は14msともう余裕が全くありません。1/60秒 = 16.67msを超えると60fpsをキープできずにコマ落ちするはずです。
f:id:takujidev:20131108191258p:plain

地面の表示だけでこの余裕のなさということは、残念ながらこのCIPerspectiveTransformによる方法はゲームには使えそうにありません。残念。

cocos2d
cocos2dではノードのquadプロパティを書き換えることでスプライトの頂点を移動する事ができますが、標準ではquadプロパティはread-onlyなので自分でセッターを実装する必要があります。

プロジェクト終了の危機を感じ、ドキドキしながら結果を見てみます。

f:id:takujidev:20131108192946p:plain

おお、2ms!

cocos2dの圧勝です。16.67msまではまだまだ余裕があります。

もしかしたらSprite Kitでもっと軽く実装する方法があるかもしれませんが、残念ながら自分には思いつかないので、cocos2dを使う事に決定しました。

パフォーマンス以外にも、cocos2dには「スプライトの各ノードがスケジュールを実行できる」とか「onExitで参照外しのためのクリーンナップができる」など便利な機能がたくさんあります。
逆にこれらをSprite Kitで実現するにはかなり面倒なことになりそうです。

参考までに、cocos2d版の地面を動かす部分の実装はこうなっています。
_repeatWidthは縞模様1パターン分の幅で、この範囲でテクスチャーを傾けたり戻したりしています。
両端が画面外に隠れているので無限にスクロールしているように見えると思います。

- (void)update:(ccTime)delta
{
    
    ccV3F_C4B_T2F_Quad newQuad = _originalQuad;
    
    GLfloat y = _touchPosition.y;
    
    GLfloat repeatWidth = 96.0;
    
    GLfloat moveX = _touchPosition.x - _winSize.width/2.0;
    GLfloat factor = -0.1;
    moveX = moveX * factor;
    
    _previousSkewX = _previousSkewX + moveX;
    _previousSkewX = fmodf(_previousSkewX, repeatWidth);

    newQuad.tl.vertices.y = y;
    newQuad.tr.vertices.y = y;
    newQuad.bl.vertices.x = newQuad.bl.vertices.x + _previousSkewX;
    newQuad.br.vertices.x = newQuad.br.vertices.x + _previousSkewX;

    _ground1.quad = newQuad;
    
}