2014年3月2日日曜日

[cocos2d-x v3.0beta2] v2.1.5からの移行メモ

 v2.1.5からv3.0beta2へ、まず動くこと、を目標にした作業メモ

 多分、cocos2d iphoneから移行してきた人向き

1)deprecated warningを削除

 全ソースからクラスプレフィックスCCを削除

 シングルトン取得メソッドの変更shared*()getInstance()

 など

2)コーディングスタイルをあわせる

 特にフィールドのプレフィックスが"m_"から"_" に変わっているため、派生クラスで利用していた場合、対処は必須

3)cocos2d::CCObject::init*(...) → cocos2d::Object::crete*(...)

 cocos2d::CCObject()派生クラスでinit()系メソッドがpublicからprotectedに変更されたため、継承クラスやインスタンスを自前でnewしていた箇所の修正が必要

 new → init()create() → retain()に比べautorelease()retain()
の呼び出しが抑えられていたのだが…、はい、誤差です

4)copyWithZone(cocos2d::CCZone pZone*) → clone()

 cocos2d::CCObjectの基底クラスであるcocos2d::CCCopyingおよびその引数として用意されていたcocos2d::CCZoneが廃止されたことからcopyWithZone()メソッドが消滅

 コピーが必要な場合、cocos2d::Clonableを継承しclone()メソッドを実装

5)cocos2d::CCSring → std::string

 cocos2d::CCStringに対応するcocos2d::Stringがないこと、事実上の吸収先とも言えるcocos2d::Valuecocos2d::Object派生でないことからstd::stringへ移行

 C++11よりCOWが廃止されたことからcocos2d::CCStringをポインタ渡ししていた箇所は確実にstd::string参照渡しとしてパフォーマンスの無用な低下を防ぐ

6)cocos2d::CCArray → cocos2d::Vector

 cocos2d::CCArraycocos2d::CCObject派生、cocos2d::Vectorは基底クラス、ということで参照カウンタがない

 ライフサイクル管理が必要な場合、std::shared_ptrを導入

 cocos2d::Vectorを簡単に言うと内部で参照カウンタ操作が実装されたstd::vectorラッパー

 主な操作はcoco2d::CCArraycocos2d::Vectorとして以下のように対応する

 objectAtIndexat
 countsize
 addObjectpushBack
 removeObjecteraseObject
 removeObjectAtIndexerase

 CCARRAY_FOREACH(array, obj)for (auto &it : vector)

7)cocos2d::CCDirectory → cocos2d::Map

 CCArrayと同じくcocos2d::CCObject派生から基底クラスになっているため参照カウンタがない

 ライフサイクル管理が必要な場合、std::shared_ptrを導入

 cocos2d::CCArrayに対してこちらは参照カウンタ操作が実装されたstd::unordered_map(もしくはstd::map)ラッパー

 主な操作はcoco2d::CCDictionarycocos2d::Mapとして以下のように対応する

 objectForKeyat
 countsize
 setObject(value, key)insert(key, value)
 removeObjectForKeyerase

 CCDICT_FOREACH(dict, obj) obj->hoge();for (auto it = map.begin(): it != map.end(); ++it) (*it).second->hoge();

8)cocos2d::CCInteger、CCFloat、CCDouble etc.

 cocos2d::CCString同様cocos2d::Valueに吸収

 これらをCCArrayで管理していた場合、std::vectorに切り替え、ポインタではなくインスタンスを格納する

 この際、参照渡しや右辺参照を使い、インスタンスの生成を押さえるとベター

9)その他

 C++11およびv3.0固有の機能に対応

 autoの利用、overrideキーワードの追加、cocos2d::Layerのイベントリスナー対応、など

2014年2月27日木曜日

[cocos2d-x v3.0beta2] XTLayerを(とりあえず)動かす

 cocos2d-xでタップやスワイプを手軽に実装できるXTLayerをv3.0でも使いたい。というわけで手抜き移行を実施。

 移行に際してやったことは以下の3点。
  • v3.0準拠へ各種名称およびコンテナを変更
  • deprecatedsetTouchEnabledおよびsetTouchModeの代替を用意
  • onExit時にEventDispatcherからEventListenerを削除

  手抜きというだけあり、onTouchCancelledおよびonTouchesCancelledは未対応。

 しかしながら 
  • setTouchEnabled(bool)xtTouchEnabled(bool) 
  • setTouchMode(ccTouchesMode)xtTouchMode(Touch::DispatchMode) 
に置き換えるとそれっぽく動く不思議。

 実際のところ、v3.0のタッチ処理はLayerで拡張するよりインターフェースから拡張する方が良さげだなぁ、と思いつつ誰かが公開してくれるのを待とう。

2014年2月24日月曜日

[tips : iPhone app] 3.2 Apps with placeholder text will be rejected

 iTunes Connectよりリジェクト報告がきたので戒めのため記録。

 JapaneseおよびEnglishでMetadataを作成した際、EnglishのDescriptionを簡易に済ませたところ、これが 3.2 Apps with placeholder text will be rejected にあたると判断されリジェクトを受けた。

 Binaryは問題ないということなので、翻訳サイトを使いEnglishのDescriptionをJapanese相当のものに修正したところ無事Reviewを通った。

 別件として、Metadataを更新する際、Default LanguageをEnglishからJapaneseに変更したところGame CenterのLeaderboard設定が無効化されたこと、うっかりしてiAdの有効化ができなかったことを併記しておく。凡ミス、良くない。

  前者はバージョンアップ、後者はAdMobやnendを別途実装して対処する予定。

[tips : cocos2d-x v3.0beta2] プロジェクト作成

 cocos2d-x v3.0で最初に「おっ?」と思うのがinstall-templates-xcode.shが見つからないことだと思います。…思いましたよね?

 実際、lsしたりfindしたり探してみても見つからないので、それよりcreate_project.pyを使いませんか、となるわけです。

 macの場合、ターミナルを開き、cocos2d-xを解凍したフォルダで./tools/project-creator/create_project.pyを実行すればフロントエンドが立ち上がります。

  あとは必要事項 - Project Nameに任意のプロジェクト名、Package Nameはandroid用のパッケージ名(必要ないならデフォルト、とりあえずならBundle Identifierなど)、Project Pathに任意のフォルダ、LanguageでC++ or Lua or JavaScript - を指定して[create]ボタンを押せば$(ProjectPath)/$(ProjectName)が生成されます。

 あとは必要なプラットフォームのプロジェクトを開いて編集すればオーケー。

2013年9月11日水曜日

[tips : cocos2d-x 2.1.5] luaを気軽に使いたい(1)

■前置き
 cocos2d-xではcocos2dx_luaテンプレートを使うことでluaベースのプログラムを無手から始めることができます。

 現在のスマートデバイスはプロセッサの速度も速く実数演算も実用レベルでこなしますからluaのみの実装で充分なケースも少なくはありません。

 とはいえc++から呼び出しすことができればシーンの初期化やイベント制御、またオブジェクトの制御をプログラムから切り離すことができ、生産性を飛躍的に向上させることが可能です。

 ではどのように実現すればいいか、今回はまずcocos2dx_luaテンプレートで目についたCCLuaEngineとその拡張元であるCCLuaStackについてかなりザックリと記しておきたいと思います。

■CCLuaEngine
 CCLuaStackにイベントやスケジューラーまわりの機能を拡張したクラスで、luaメインで組む場合に使用します。

 CCLuaEngine固有の機能はc++から呼び出すにあたり無駄でしかありませんので素直にCCLuaStackを使った方が良さそうです。

■CCLuaStack
 luaのスレッド(実質VM)であるlua_Stateをcocos2d-x向けに拡張したものですが、CCLuaEngineの基礎として設計されているため欲しい機能が全てある、という状態ではありません。

 ただし、cocos2d-xの多くのクラスがそうであるように継承による調整は容易です。

 なお、継承する際は、luaからcocos2d-xの機能を利用するために用意された68,000行に迫るグルーコードの存在を意識しておきましょう。

※グルーコードをザックリ説明すると、スクリプト言語(lua)から呼び出されるネイティブ言語(c++)の実行関数と登録コードのことです

※グルーコードに興味がある方は"$(PROJECT_NAME)/libs/lua/cocos2dx_support/LuaCocos2d.cpp"を開いてみましょう

 グルーコードはtolua++というツールで自動生成され、非常に多くの恩恵を授けてくれますが、その反面コードサイズが増加し、CCLuaStackの生成にも相応の処理時間が必要となります。

 これは多くの場合取るに足らない程度のものですが、例えば、古いデバイスで、弾ごとにスクリプトを持たせ、弾幕を生成する、といった場合、処理落ちを覚悟する必要はあるでしょう。

 そうなってからでは遅いのであらかじめテストしておいた方が無難、という程度の話ではあります。

 次回は実際にluaスクリプトを動かしてみたいと思います。

2013年9月5日木曜日

[tips : cocos2d-x 2.1.5] マルチタッチ対応

 cocos2d-xで2点以上のタッチを制御したいな、ということでさっそく実装。とりあえずiOSのみ。

■環境
  • cocos2d-x 2.1.5
  • iOS6.0.1(iPod touch 5G) 
■実装
  • AppController.mmに以下を追加。
    __glView.multipleTouchEnabled = true; // __glView定義直後でOK
  • CCLayer派生クラスで以下のメソッドを定義
    virtual void ccTouchesBegan(cocos2d::CCSet *touches, cocos2d::CCEvent *event);
    virtual void ccTouchesMoved(cocos2d::CCSet *touches, cocos2d::CCEvent *event);
    virtual void ccTouchesEnded(cocos2d::CCSet *touches, cocos2d::CCEvent *event);
  • CCLayer派生クラスのinitに以下の処理を追加
    setTouchEnabled(true);
    setTouchMode(kCCTouchesAllAtOnce);
  • CCLayer派生クラスに定義したメソッドを実装
// 実装の一例

/*!
 *  @brief  タッチ開始デリゲート
 *  @param  [in]  touches ある瞬間にタッチされた情報(allTouchesではない)
 *  @param  [in]  event   未使用
 */
void MyLayer::ccTouchesBegan(CCSet *touches, CCEvent *event)
{
    CC_UNUSED_PARAM(event);

    for (CCSetIterator it = touches->begin(); it != touches->end(); ++it)
    {
        CCTouch *touch  = (CCTouch *)(*it);
        int      id     = touch->getID(); // 0 to (CC_MAX_TOUCHES - 1)
        m_touchFlag[id] = true;                 // タッチ状態に遷移
        m_touchAt  [id] = touch->getLocation(); // 位置を保存
        // 以下、必要な情報を取得し、update内で処理してやると良い
    }
}

/*!
 *  @brief  タッチ移動デリゲート
 *  @param  [in]  touches ある瞬間に移動した情報
 *  @param  [in]  event   未使用
 */
void MyLayer::ccTouchesMoved(CCSet *touches, CCEvent *event)
{
    CC_UNUSED_PARAM(event);

    for (CCSetIterator it = touches->begin(); it != touches->end(); ++it)
    {
        CCTouch *touch = (CCTouch *)(*it);
        int      id    = touch->getID(); // 0 to (CC_MAX_TOUCHES - 1)
        m_touchAt[id]  = touch->getLocation(); // 位置を更新
        m_delta[id]    = touch->getDelta();    // 移動値を取得
        // 以下、必要な情報を取得し、update内で処理してやると良い
    }
}

/*!
 *  @brief  タッチ終了デリゲート
 *  @param  [in]  touches ある瞬間に離された情報
 *  @param  [in]  event   未使用
 */
void MyLayer::ccTouchesEnded(CCSet *touches, CCEvent *event)
{
    CC_UNUSED_PARAM(event);

    for (CCSetIterator it = touches->begin(); it != touches->end(); ++it)
    {
        CCTouch *touch  = (CCTouch *)(*it);
        int      id     = touch->getID(); // 0 to (CC_MAX_TOUCHES - 1)
        m_touchFlag[id] = false;         // タッチ状態を終了
        // 以下、必要があれば情報を初期化する
    }
}
  • ビルドして動作確認
  • あとは実装次第

2013年9月1日日曜日

[tips : cocos2d-x 2.1.5] 停止したBGMがバックグラウンドタスクからの復帰で再生される

■現象
  • 停止したはずのBGMが鳴り始める

■環境
  • cocos2d-x 2.1.5
  • SimpleAudioEngine
  • iOS5.1.1(iPod touch 3G)、iOS5.1(iPod touch 4G)、iOS6.0.1(iPod touch 5G)
※.iOS4.3.3(for iPad)では未解決

■手順
  • SimpleAudioEngine::playBackgroundMusicでBGMを再生する
  • SimpleAudioEngine::stopBackgroundMusicでBGMを停止する
  • ホームボタンを押しタスクをバックグラウンドに移行する
  • タスクを選択し再度フォアグラウンドに移行する

■原因
  AppDelegate::applicationDidEnterBackgroundで呼び出されるSimpleAudioEngine::pauseBackgroundMusicおよびAppDelegate::applicationWillEnterForegroundで呼び出されるSimpleAudioEngine::resumeBackgroundMusicの挙動が原因。

 SimpleAudioEngine::resume最終的にAVAudioPlayer::playするだけであるためSimpleAudioEngine::stopBackgroundMusicで停止したはずのBGMが再生される。

■対応
 サウンドヘルパークラスで吸収。主な処理は以下の通り。

  • BGM再生フラグを持たせ、playでtrue、stopでfalseにする
  • resumeされた場合、BGM再生フラグをみてSimpleAudioEngine::resumeを呼び出すかを決定する

 SimpleAudioEngine.mm/hを直接変更する場合は以下の通り。

 SimpleAudioEngine::stopBackgroundMusicの代わりに無音のBGMを再生する、もしくはミュートするという強引な解決法も存在するが少なくともライブラリとして実装することはオススメできない。

■その他
 ゲームの一時停止機能でpause / resumeを利用したいとして、AppDelegate::applicationWillEnterForegroundresumeBackgroundMusicが強制的に呼び出されてしまう件に関してはまた別の機会にでも。