Cocos2d-x v3.0の変更点に対応する

こんにちは。野口です。
業務アプリプログラマーがCocos2d-xでゲームを作るシリーズをお届けします。

年初にCocos2d-xのメジャーバージョンアップである、cocos2d-x v3.0がalpha版からbeta版へと移行し、今月(2014年4月)遂に、めでたく正式版のcocos2d-x v3.0がリリースされましたっ!!
本ブログではcocos2d-x v2.x系での記事を書いていましたが、これから作成するゲームをv2.x系で作ることはまずないでしょう。
ということで、私もこれまで2.xで作っていたアプリを窓から投げ捨ててv3.0の世界へ果敢に挑戦していこうと思います。

[ Cocos2d-x v3.0で作る横スクロールシューティング ]

前回作ったシューティングゲームをv3.0で書き直しました。

ソースコードはGitHubで公開しているのでご希望の方は以下から取得してください。
(cocos2d-x v3.0beta2、Xcode5.0のiPhone Retina(4-inch 64bit)環境で動作確認しています)
https://github.com/noguchi999/shooting2.git

v3.0の変更点

ソースを書き直すにあたっては、v3.0で何が変わったのかを知らないとお話になりません。
まずは本家サイト(http://www.cocos2d-x.org/)やGitHubのCocos2d-xプロジェクト(https://github.com/cocos2d/cocos2d-x)で公開されているリリースノートをモソモソと翻訳して変更点を確かめます。

ハイライト

  • Objective-Cっぽい書き方からC++ (C++11)っぽい書き方に変更
  • ラベルの改善
  • レンダラの改善(v2.2よりずっと速い!!)
  • 新しいイベント・ディスパッチャ
  • 物理演算の統合
  • 新しいUIオブジェクト
  • JavaScriptのリモートデバッガの導入
  • リモートコンソールのサポート
  • 画像処理の見直し(メモリの解放やファイルフォーマット毎のapiの統一)
  • Lua Bindingのジェネレータ(ソースコードの自動生成)
  • テンプレート対応のコンテナクラス

さすがにメジャーバージョンアップだけあって盛りだくさんですね。
上から順番に眺めつつ、対応を考えていきましょう。

Objective-Cっぽい書き方からC++ (C++11)っぽい書き方に変更

主だったところでは、以下のような変更が挙げられています。

クラス名やメソッド名の変更

クラス名の変更とは、具体的にはクラス名のサフィックスが取り除かれました。
(CCScene → SceneとかCCSprite → Spriteとかになりました。)
Objective-Cではnamespaceがないため、名前の衝突を避けるために
クラス名のプレフィックスとして独自の2文字(NeXT STEPを略してNSとか、CocosだからCCとか)を付けることが慣例となっています。

しかし、C++にはnamespaceがあるので、そもそもそんなことをする必要はないということでゴッソリ取り外されたようです。
取り敢えずgrepしてreplaceするだけの簡単なお仕事を済ませてから「Build Success」と表示されることを祈りつつcommand+Bします(←ダメな見本)

また、ゲッターメソッドの名前もこれまでの
Objective-Cライクな名称から変更されました。
例えば、シングルトンクラスであるDirectorからインスタンスを受け取るための
メソッドは、sharedDirectorからgetInstanceという名称に変更されました。

コールバックにstd::functionやlambda式が使えるように

これは嬉しい変更点ですね。ちょっとしたコールバックの処理のために関数を定義しなくても良くなりました

// v2.x系
CCCallFunc *action1 = CCCallFunc::create( this, callfunc_selector( MyClass::callback_0 ) );

// v3.x系
auto action1 = CallFunc::create(
                 [&](){
                     auto s = Director::sharedDirector()->getWinSize();
                     auto label = LabelTTF::create("called:lambda callback", "Marker Felt", 16);
                     label->setPosition(ccp( s.width/4*1,s.height/2-40));
                     this->addChild(label);
                 }  );

// v3.x系 関数を指定することも勿論できます
auto action1 = CallFunc::create( CC_CALLBACK_0(MyClass::callback_0,this));
enum classが使えるように

C++11の新規格であるscoped enumerationが使えるようになりました。
これで名前の衝突を避けるためにConfig.hにkから始まる奇妙な名前のあんちくしょう達を羅列する作業から解放されます。
これに伴い、Cocos2d-x各種の定数名も改善されており、例えば以下の
ように変わっています。

| v2.1                             | v3.0                             |
| kCCTexture2DPixelFormat_RGBA8888 | Texture2D::PixelFormat::RGBA8888 |
| kCCDirectorProjectionCustom      | Director::Projection::CUSTOM     |
| ccGREEN                          | Color3B::GREEN                   |
| CCPointZero                      | Point::ZERO                      |
| CCSizeZero                       | Size::ZERO                       |
overrideやfinalキーワードが使えるように

これまたC++11の新規格であるoverrideとfinalキーワードが使えるようになりました。
これにより、クラスの継承やvirtual関数のオーバーライドの制御を明示的に行うことが可能になりました。
これにより複数人での開発がより容易になることでしょう。

ラベルの改善

ラベルの改善とはつまり、Cocos2d-xで画面に文字(ラベル)を描画するための以下のクラスに関する改善です。
LabelTTF / LabelBMFont / LabelAtlas
それぞれ利用する際のAPIが統一され、iOSやAndroidといったプラットフォームによって描画結果が異なるといった問題が改善されました(未確認)。
それとキャッシュを利用することによりパフォーマンスも改善されたようです。

レンダラの改善(v2.2よりずっと速い!!)

パフォーマンス面での大きな改善点として、以下が挙げられています。

  • SpriteとParticleSystemのオブジェクトを自動でバッチ化するよ!(同じテクスチャを連続で使うときだけ)
  • 画面外でのSpriteの描画のような無駄な処理をチェックして選別することでパフォーマンスをアップするよ!

上の自動バッチ化による改善の結果、前回の記事で紹介したSpriteBatchNodeを使う必要がほぼなくなりました。
リリースノートによると、v3.0以降ではSpriteBatchNodeは本当にギリギリ、ほんのわずかでもパフォーマンスをアップしたい場合の最後の手段としてのみ使うように書かれています。
前回の記事で紹介したばっかりの俺涙目。でも、使い分ける手間が省けて便利になったのは望ましいことです。

その他の改善点としては、「グローバルZオーダー」の導入が挙げられています。
NodeクラスにsetGlobalZOrder()とgetGlobalZOrder()という2つのメソッドが追加されました。
従来のZオーダー(ローカルZオーダー)では、Nodeの親子関係で描画順が変わってしまいます。
そのためシューティングゲームで敵を倒したときの爆発のエフェクトを敵スプライトの子として追加すると、敵スプライトの裏に爆発エフェクトが隠れてしまうという困ったことがありました。
グローバルZオーダーを利用することにより、このような場合も素直なプログラミングが行えるようになりました。

因みに、グローバルZオーダーの値はfloatであるという点と、小さい値の方が優先度が高いという点がローカルZオーダーと異なるので注意が必要です。

新しいイベント・ディスパッチャ

イベント・ディスパッチャと言えば、なんといってもタッチイベントの処理が一番の関心事です。
タッチイベント(に限らずですが)の書き方はv2.xから大きく変わり、イベントリスナーを作成してイベントディスパッチャへ登録するという、
より洗練された(Java ScriptやAction Scriptでイベント処理を書きなれた人にとっては)馴染みのある方法に変わりました。

// 画面をタップされたイベントを受け取りたい場合は、このように書きます
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);

// タッチイベントを受け取るスプライトも簡単に作れるように(lambda式でコールバックも書けます)
auto sprite = Sprite::create("sprite.png");
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = [](Touch* touch, Event* event){
        // event->getCurrentTarget() returns the *listener's* sceneGraphPriority node.
        auto target = static_cast(event->getCurrentTarget());

        //Get the position of the current point relative to the button
        Point locationInNode = target->convertToNodeSpace(touch->getLocation());
        Size s = target->getContentSize();
        Rect rect = Rect(0, 0, s.width, s.height);

        //Check the click area
        if (rect.containsPoint(locationInNode))
        {
            log("sprite began... x = %f, y = %f", locationInNode.x, locationInNode.y);
            target->setOpacity(180);
            return true;
        }
        return false;
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, sprite);

物理演算の統合

Cocos2d-xの物理演算エンジンと言えば、Box2DとChipmunk2Dがあり利用者で「どちらか選べぇ」という状態だったわけですがv3.0からChipmunk2Dに統一されました。
今回のシューティングは関係ありませんが、Box2D使いだった全俺が号泣。
とは言え、普通にCocos2d-xで物理演算を利用するゲームを開発する分には物理エンジンが何かとか気にする必要はないので大丈夫です。

新しいUIオブジェクト

リリースノートにはなぜか詳細な記述がないのですが、おそらくはCocoaでお馴染のUIScrollViewやUIPageViewControllerのような表現を可能にする各種GUIウィジェットのことだと思います。
ハッキリ言って、この機能追加は超うれしい。

UILayout / UIScrollView / UIListView / UIPageViewその他もろもろ
UIButtonなどというのもあります。

詳しくはこちらを
http://www.cocos2d-x.org/docs/manual/framework/native/gui/container/en

テンプレート対応のコンテナクラス

Java ScriptやLua関連は申し訳ないのですが、スルーで。

これも大きな変更ですが、v2.x以前で利用されていたCCArray、CCDictionaryといったCocos2d-x拡張のコンテナクラスは廃止となりました。(一応、まだ使うことはできます。)
今後は、CCArrayではなくcocos2d::Vectorを、CCDictionaryではなくcocos2d::Mapを利用することが推奨されています。
また、その他の値を扱うクラス(CCString、CCBool、 CCFLoat、 CCDoubleなど)も廃止されcocos2d::Valueに置き換わりました。

使い方は至ってシンプルで、C++のstd::vectorやstd::mapと同じように使えます(CCARRAY_FOR_EACHのような奇妙なマクロを使う必要はなくなりました)
ただし、コンテナに入るオブジェクトはcocos2d::Object型のポインタのみで、その辺の制約はCCArray、CCDictionaryと同様です。

// cocos2d::Vector
Vector vector;
auto sp0 = Sprite::create();
vector->pushBack(sp0);
for(auto sp : vec1)
{
    log("sprite tag = %d", sp->getTag());
}

// cocos2d::Map
Map map;
std::string mapKey0 = "MAP_KEY_0";
auto sp0 = Sprite::create();
map.insert(mapKey0, sp0);
for(auto key : map.keys())
{
    auto spTag = map.at(key)->getTag();
}

// cocos2d::Value
Value val0(65);     // int
Value val1(3.5f)    // float;
Value val2("hoge"); // string
Value val3(true);   // boolean

それぞれのより詳しい使い方はこちらを
・cocos2d::Vector
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/data-structure/v3/vector/en.md

・cocos2d::Map
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/data-structure/v3/map/en.md

・cocos2d::Value
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/data-structure/v3/value/en.md

さいごに

いかがでしたでしょうか? なかなか変更点が多くてv2.x系で作ったゲームをv3.0系に移行するとなると大変ですね。私のアプリも未だにCCStringやCCArrayとか残っちゃっていますが・・・。 また、残念ながら本記事執筆時点ではv3.0対応の日本語書籍が出版されていないこともあり、情報収集はまだまだ大変だというのが現状の正直な感想です。

とは言え、Cocos2d-xは本家プロジェクトで(英語ですが)サンプルコード付きのドキュメントが充実していますし、容易にビルドして動かしてみることの出来るテストコードも提供されているため、かなり勉強する環境は整っていると思います。 Cocos2d-x v3.0正式版がついにリリース。

そろそろ本腰を入れてv3.0に移行しようと考えている方々のお役に立てればと願いつつ、本記事を締めさせていただきます。

コメントを残す

メールアドレスが公開されることはありません。

ABOUTこの記事をかいた人

アバター

僕と契約してアーティストになってよ。 普段はJavaやPHPで業務Webアプリばっかり作っています。 安西先生、Rubyで仕事がしたいです・・・。