Cocos2d-x:v3.10でのAndroid開発環境メモ

Android studioに挑戦-あきらめ

cocos2d-xはv3.7からAndroid studioに対応したらしい。eclipseは非推奨となり今後サポートされなくなるのでAndroidStudioへの移行が必要。
AndroidStudioのインストールは成功したが、Runするとエラーになってしまう。今回は時間がないので使い慣れたeclipse(luna)を使うことにした。
今振り返るとNDKの設定をデフォルトから変更すべきだったのかもしれない。
Android studioでのNDKの設定はprojectを開いている状態で、メニューのFile->project structureを選択し、表示されるウインドウの左カラムからsdk locationを選択する(下図)。
現状は画像のようにバンドルされていたデフォルトのNDKを利用しているが、Cocos2d-xの場合は少し古いNDKでないとビルドできないことが多いので
ASsdk
 

bash_profileの設定

bash_profileはmiで編集するのが便利。miは「不可視ファイルを開く」があるので。
cocos2d-x:v3.10をビルドできた設定は以下の通り(パス部分のみ抜粋)。
ポイントはNDKのバージョン。以下のように「r10e」をダウンロードしてきて利用した(現時点での最新はr11cでv3.6で利用していたのはr9d)。
SDKはAndroidstudioで管理しているsdkへのパスにした。
——————————————————————
export NDK_ROOT=/Users/designdrill/androidDev/android-ndk-r10e
export ANDROID_SDK_ROOT=/Users/designdrill/Library/Android/sdk
export ANT_ROOT=/Users/designdrill/androidDev/apache-ant-1.9.7/bin
——————————————————————
少し躓いたのはantの設定。これは上記のようにbinまでパスに含める必要がある
→参考:[Mac]Cocos2d-x開発環境構築

eclipseの設定

eclipseの最新版(最終版?)はmarsだがメニュー構成が結構異なるので、使い慣れたlunaを利用することにした。
sdkへのパスは現状eclipseにバンドルされたsdkを利用(下図)しているが、これはAndroidStudioの方に合わせた方がよいかもしれない(bash_profileもAndroidStudioのにしているし)?。sdkは同じURLから更新されるので問題ないと思うが、結構ファイルサイズが大きいし、各々でバージョンアップしなければならないので。
eclipse
eclipseのsdkの設定はpreferencesのAndroidから行う(上図)。
 
ちなみにAndroid SDKへのURLは現在Android studioへのリンクになっているが、Android sdkは単体でもダウンロードが可能(android-sdk-macosx:現時点最新はr24.4.1)。Android Stdioのサイトにある「Download Options」をクリックすると単体のandroid-sdkが置いてある場所に移動します(下図)。
macsdk

Nend広告の組み込み

まずはNend広告を組み込まずに実機プレイまで可能なことを確認。この段階でAndroid固有のバグがないかを確認する。
バグがないことを確認したらNend広告の組み込み。build_native.pyで生成されたフォルダなどは削除せずに上書きビルドする。
このときAndroidManifest.xmlにgoogle-play-serviceの設定があるとビルドを通らないので、これはコメントアウトしてeclipseで読み込んだ後に有効にする(たぶん私の環境設定の不備のためだと思う)。
あと今回躓いたのは以下の点!

Nendのバナー広告だけ出ない!

現時点で最新のnend_java(net/nend/NendModule:日付2015/10/16)だとbuild_native.pyが通らない。なので1つ古いnend_java(日付:2015/10/1)を利用してbuild_native.pyを通した
しかし、これだとバナー広告が表示されない(インターステイシャルは表示される)。なのでbuild_native.pyを通してから、eclipseに読み込む前にNendModule.javaだけを新しいファイル(日付:2015/10/16)のに差し替えた。するとバナーも表示された。
nend
そもそも最新のNendModule.javaがビルドを通らないのがいけない。またNDKのバージョンなどが関係しているのだろうか?
とりあえず上記の対応で安定して機能しているので良しとしよう。
 
 
 

Cocos2d-x:v3.6だとOK、v3.10だとクラッシュ

結論:100fpsとか無理だから!

 

弾幕ゲームの制作を始めた頃(Cocos2d-x v3.2)の問題

弾の移動処理はupdate関数内に記述していた。しかし弾の移動速度が微妙に揺らぐ(カクつく感じではなくsinカーブのような緩急がつく感じ)。人によっては気づかないレベルだが、見ていると気持ち悪くなる(乗り物酔いの感じ)。

schedule関数に切り替える

弾の移動処理をschedule関数内に記述し、実行間隔を1/60秒(60fps)に設定。しかしupdateと同じ症状。

100fpsに!!

scheduleの実行間隔を0.01秒(100fps)に変更すると揺らぎが無くなった!!
弾幕の光、弾幕の器、弾幕の器2の弾の移動処理はすべて100fpsで処理されている。
Android5への対応でCocos2d-xのバージョンを3.6に変更したが、100fpsの設定は変更無し。

100fpsでクラッシュ

弾幕の檻Android版からCocos2d-xのバージョンを3.10にアップ。実機でアプリがクラッシュ。iOSでもクラッシュするのでAndroid固有の問題ではない。
100fpsの処理を60fpsにしたらクラッシュしなくなった。そして3.2から苦しめられていた微妙な揺らぎも発生しない!
 
 

そもそも60fps以上を設定することが間違い

スマホの描画は60fpsが最大(ゲーミングPCのように120fpsとか無理!)だと思うので、100fpsで弾を移動させること自体が間違い。100fpsでの処理についてはずっと気になっていた。前回の記事(計算負荷を減らす)のなかでも問題にしており、揺らぎがギリギリ気にならない83fpsまで頻度を落としている。
今回iOS版(Cocos3.2:fps83)とAndroid版(Cocos3.10:fps60)の比較動画を作成したが、83fpsと60fpsに大きな差はない。やはり60fps以上の設定は無意味。でも以前のCocosでは気持ち悪い揺らぎが発生して、仕方なく無茶なfpsを設定

 
 

詳細

すぐにクラッシュするのではなく1秒くらい経過してからクラッシュ
クラッシュの直接の原因はschedule内でthisがspriteとして参照できなくなるため(下図)。
スクリーンショット 2016-05-11 0.00.47
 
正常な場合は以下のようにthisはspriteとして参照できている。
スクリーンショット 2016-05-10 23.59.38
 

その他

・fpsが高くても1秒は耐える。という挙動が気になったので、アプリでは58fpsと微妙に処理頻度を落とした。
・schedule間隔が0.0になっている場所があった(fps無限大!)しかし、ここではクラッシュしなかった(何故?)。
 ※計算式でschedule間隔を設定しており、その計算式が間違っていた。
 ※炎弾のアニメ処理で3.10で高速にアニメが再生され不審に思い調べて問題が判明。
 
 
 
 

Cocos2d-x:計算負荷を減らす

要約

・ルートの計算を避けてgetDistanceを使うのをやめる。
・座標系は回転させていないのでconvertToWorldSpaceを使わなくても何とかなる。
 
 

fpsの低下を検知して演出を変更する

今作成しているゲームは「弾幕の器」シリーズよりもたくさんの弾を表示します。そのため古い機種では60fpsを維持できなくなってきました。なのでfpsの低下が一定時間続くと「光の演出」をOFFにする処理を追加しました。fpsの測定はupdateの引数deltaで計算可能です。具体的には以下のようなコードを書きました。

--------------------------------------------------------------------
void GameScene::update(float delta){//----deltaには前回のコールからの差(秒)が渡される
 if (delta > (1.0/40)){//---fpsが40を下回った?
  fpsDelayCnt++;
  if (fpsDelayCnt >= 40){//----40fps以下の速度低下が約1秒継続した
   //---画質を落とす処理
   this->unscheduleUpdate();//---fpsのチェックはやめる
  }
 }else{
  fpsDelayCnt=0;
 }
}
--------------------------------------------------------------------

 

弾道の計算頻度

次は計算頻度の見直しです。これまでの弾の移動とあたり判定の処理は以下のように1秒間に100回の頻度で行っていました。60fpsだから移動頻度は1/60秒のハズなのですが「1/60だと微妙に速度が揺らぐ」感じがするのです。動きが各つくのではなく、sinカーブ的に速度が緩く揺らぐ気がするのです。

--------------------------------------------------------------------
this->schedule(schedule_selector(EnemyBullet::hitTest), 0.01);
--------------------------------------------------------------------

デザインにおける「モアレ」と似たような(周波数的な)現象なのかな?と思い、数値を微妙に変えていったのですが60fps界隈では違和感は消えませんでした。結局、違和感が気にならないレベルは以下のように0.012でした。少しの変更ですが%的には約16%(100fps→83.3fps)の負荷が減ります

--------------------------------------------------------------------
this->schedule(schedule_selector(EnemyBullet::hitTest), 0.012);
--------------------------------------------------------------------

 

あたり判定の再考

「弾幕の器」でのあたり判定は「丸弾は2点間の距離」と「矩形はベクトル的な処理」に分けていました。しかし今回の弾幕ゲームでは、すでにfpsを維持するのが難しいので「矩形はベクトル的な処理」は廃止します。つまり長方形の弾は出ません、十字型の弾が登場しますが、実は緑丸にしかあたり判定がありません!
d2
矩形のあたり判定については以下の記事を参考にしてください。重そうな処理でしょ?
→参考:弾幕ゲーム第2弾:矩形のあたり判定
→参考:cocos2d-x:当たり判定のすり抜け対応
 
 

getDistanceの利用をやめる

ということで、あたり判定の処理は「2点間の距離」のみになりました。Cocos2d-xでは2点間の距離を求めるメソッド「getDistance」があり、大まかには以下のようなコードになります。

--------------------------------------------------------------------
Point shipPos = spShip->getPosition();
float shipDist = shipPos.getDistance(this->getPosition());
if(shipDist < 10){//---弾と敵の距離が10より小さくなったら被弾
 //---被弾処理
}
--------------------------------------------------------------------

getDistanceは距離を求めるため「計算にはルートが含まれます」。しかし、あたり判定はルートを省略できるためgetDistanceを使うことを廃止し以下のように書き換えました。両辺を2乗した形です。

--------------------------------------------------------------------
sqrt(x^2+y^2) = distance;
 ↓
x^2+y^2 = distance*distance
--------------------------------------------------------------------

また、Y座標だけ先に計算して範囲外だったらX座標の計算を省略できるようにしました、以下のような感じです。

--------------------------------------------------------------------
//---まずは弾と敵のY座標の距離だけを算出
float ship_y = shipPos.y;float my_y = myPos.y;
float dist_y = ship_y - my_y;
float dist_y_abs = dist_y*dist_y;

if (dist_y_abs < 10*10){//----ルートするのではなく距離の方を2乗したほうが計算負荷は低い
 //----Y座標の距離が範囲内にある時だけX座標の計算をする
 float ship_x = shipPos.x;float my_x = myPos.x;
 float dist_x = ship_x - my_x;
 float dist_x_abs = dist_x*dist_x;
 if ((dist_y_abs+dist_x_abs)<(10*10)){//---ここでもルートではなく2乗
  //---被弾処理
 }
}
--------------------------------------------------------------------

 

convertToWorldSpaceを利用するのをやめた

砲塔は敵母艦内に配置されており自弾と座標系が異なるためconvertToWorldSpaceで座標を算出し直していました。convertToWorldSpaceの処理内容を確認すると「よく分からない3次のベクトル計算(座標系が回転していてもOKな感じの式)」があったので利用するのをやめました。今回は座標系は回転していないので加算のみで対応できるため自作した方が処理が軽くなります。大まかには以下のような変更です。

--------------------------------------------------------------------
Point fortLocalPos = spFort->getPosition();//---母艦内の砲塔の位置を取得
Point fortPos = this->convertToWorldSpace(fortLocalPos);//---グローバル座標に変換
--------------------------------------------------------------------



--------------------------------------------------------------------
Point fortLocalPos = spFort->getPosition();
Point fortPos = fortLocalPos + enemyShipPos - Point(center_x,center_y);
--------------------------------------------------------------------

enemyShipPosは母艦の座標(母艦はグローバル座標に配置されている)。Cocos2d-xでは画像の中心が原点なので、最後の項のように中点座標を引く必要があります。
 
 

古い機種でも遊べるようになったと思う

上記の配慮をしなかった時と比べると、体感できるほど処理が軽くなったよ。
 
 
 

メモ

いつの日かUnity

今ゲーム制作で利用しているCocos2d-x(C++)は弾幕ゲームのようにたくさんのスプライトを動かすのに適している(動作負荷が軽い)。今後制作するカジュアル系のゲームも微妙に弾幕要素が混ざるので今後もCocos2d-xを使い続けるのだけれど、Unity向けのアイデアも色々浮かぶ。ボクセルのゲームは楽しそう。

Webデザイン

ゲーム制作の話ばかりですが、Webデザインも大切なお仕事。ということで気になった記事をメモ。ノンデザイナーズデザインブックのUI版的な記事です。
→参考:優れたUIを実現するために確認しておくべき51のチェックリスト
 

Github

Githubの説明をRPGで学ぶサイト。
→参考:ギットクエスト
 
 
 

Cocos2d-x:ビットマップフォント作成

久しぶりにビットマップフォントを作成しようと思ったらハマったのでメモ。以前にもメモしたのですが言葉足らずな感じなので…。今回もShoeBoxを利用します。
→参考:以前のメモ(2014.9/8)
 

pngは分割しない

spriteFrameを作成するときはバラバラのpngデータを利用しますが、ビットマップフォントの場合は「1つにまとめたpngデータ」を用意します。今回用意したのは以下のような画像(分かりやすいように黒背景にしていますが、実際のデータは透明背景です)。
gameText
 

ShoeBoxの設定

1枚のpngファイルが分析されて自動分割されるので、それに合わせて「Txt Chars」を設定しなければなりません。メインウインドウ内(下画像で一番小さいウインドウ)に対応状況が表示される(黒縁の白文字)ので間違いがないか確認できます。
またデフォルトのカーニング設定は文字間隔が狭いので調整した方がよいでしょう(Kerning Valueの値を調整する)。
blog0227

.fntファイルの修正

shoeBoxでbitmapフォントを作成するとpngファイルの他に「fntファイル」が書き出されるのですが、このファイル内のパスが絶対パスになってしまうので、これを修正します。
スクリーンショット-2016-02-27-3.19
fileの値はfntファイルからみたpngファイルの相対位置なので、同じ階層に配置するのであればファイル名だけでOKです。あと問題はないと思うのですが、一番最初の「face」の項目も同じように修正しておきましょう。
 

Cocos上での表記

ビットマップフォントの表示をWeb上で検索すると「CCLabelBMFont」を利用した方法が出てきますが、これは非推奨になっているので以下のようにcreateWithBMFontメソッドを利用します。
++++++++++++++++++++++++++++++++++++++++++++++++++
string strTimeScore = StringUtils::format(“%d”,myTime);
Label* lbTimeScore = Label::createWithBMFont(“images/numFont.fnt”,strTimeScore);
lbTimeScore->setPosition(120,136);
endNode->addChild(lbTimeScore);
++++++++++++++++++++++++++++++++++++++++++++++++++
今回は得点表示なので、1行目で得点を文字列に変換しています。
2行目でビットマップフォントを作成しています。今回利用するnumFont.fntはCocosのResources内のimagesフォルダに入れたので上記のように「パス」を記述しました。3行目で位置を設定し、4行目で表示しています。

以上。

Cocos2d-x:ゲームの一時停止

意外と面倒

まず参考になったのは以下のサイト。ただしpauseSchedulerAndActionsなどは現時点では非推奨になっているので代わりにpauseやresumeを利用する必要がある。
→参考:open hope様「ゲームをポーズ(一時停止)しよう
 
注意すべき点は以下の2つ。
ポーズボタン自体も無効化してしまうので別途resumeをする必要がある
子スプライトに伝播しない
 
現在作成しているゲームでは孫スプライトまで停止させる必要があるため以下のように記述した。孫はgrandChildだけど長いのでmagoにしています。
***********************************
for (auto* childs : this->getChildren()){
 Sprite *spriteChilds = (Sprite *)childs;
 spriteChilds->pause();//———————–子供:ポーズ
 for (auto* mago : spriteChilds->getChildren()){
  Sprite *spriteMagos = (Sprite *)mago;
  spriteMagos->pause();//——————–孫:ポーズ
 }
}
***********************************

円弧の体力ゲージ

Cocos2d-xでは円弧のプログレスバーを作成できると聞いたのでしらべてみた。
本家のサイトに行ってみるものの良くわからない。setzTypeメソッドを使うと思うのだけれど、値のリストが見当たらない…。
→参考:本家「ProgressTimer Class Reference
 
さらに調べたら以下のサイトを見つけた。円弧の設定もある!
→参考:プログレスバーに使えるCCProgressTimerクラス
 
あと円弧ではないけれど動画も見つけたのでメモ(かなり古いけれど…)。

Xcode7:does not contain bitcode

Xcode7で既存のCocos2d-xプロジェクトを読み込みiOS9.2の実機テストをしようとしたところ「does not contain bit code」のエラーがでる。有名なエラーのようで検索すると情報がたくさん出てくる。
→参考:google検索「Xcode7 does not contain bitcode
 
これはビルド設定のENABLE_BITCODEをNOにすると解決するが、ENABLE_BITCODEを設定する場所がわかりにくいのでメモ。まずは以下のように「Project」を選択する。
スクリーンショット-2016-02-11-14.21
 
そのProjectにて「Build Setting」を選択する。ここで注意するのが初期の状態ではENABLE_BITCODEの設定が表示されないので左肩の部分をBasicから「All」に変更する。するとBuild Optionsの中にENABLE_BITCODEの項目は表示されるので、これをNoにすればOK
スクリーンショット-2016-02-11-14.21

シングルトン

各クラスから共通に利用できる

Flashでは情報管理用のMCを作成していましたがCocos2d-x等ではシングルトンを使うのですね。デザインパターンは効いたことがありますが利用するのは初めてです。
→参考:Cocos2d-xでシングルトンによる画面間データ連携
 

構造体

ActionScript2.0 → jQuery(javaScript)と渡ってきた私は構造体も初体験。初利用のシングルトンに利用したためエラーに苦しめられることに。
***************************************************
public:
 struct normalData{
  int bType[3];
  int bNum[2];
  float speed[3];
 };
 normalData normalPattern[100];
***************************************************
構造体を作成する「struct文は型を設定」すると理解すると良さそう。上記ではnormalDataという(構造体の)型を作成している。struct文は型を設定するだけなので。変数の宣言も行わないと行けない。そこで最後の行でnormalData型の変数normalPatternを作成している(サンプルでは100個分の配列として)。
 
この変数normalPatternはシングルトンの変数として外部から参照するためpublicとして設定する必要がある。