[Cocos2d-x] iOSで作成したものをAndroidに移植した時のメモ


まず最初にiOSで作り始め、ある程度完成した段階でAndroidへ持って行こうとしたときにハマったのでそのメモです。
色々一筋縄ではいかない様子。

## Android.mkを編集する

Cocos2d-xが最初に生成するAndroid Projectはある程度準備されています。
その中でも、`Android.mk`は「Makefile」で、セットアップに使われます。

`Android.mk`に設定されているのはデフォルトのものなので、作成した当初のファイル名（`HelloWorld`など）が残ったままになっています。
なので、まずは`LOCAL_SRC_FILES`と書かれている部分を、実際に読み込ませたいファイル一覧に書き換えます。

```Android.mk
LOCAL_SRC_FILES := swipedefendercpp/main.cpp
LOCAL_SRC_FILES += $(CPP_FILES:$(LOCAL_PATH)/%=%)
```

ちなみに2行目の部分は、以下の記事を参考に、作成したcppファイルを自動的に読み込む設定を行っています。


### 必要なC++ファイルを自動的に読み込ませる

こちらの記事（[Cocos2d-xで新しく作ったクラスを毎回Android.mkに追加せずに済ます](http://syszr.com/s44.html)）を参考にさせて頂きました。

## build_native.phを使ってビルド

Android向けプロジェクトは`build_native.py`を使って`Classes`ディレクトリに入っているC++のファイルをビルドして利用するようです。

そのため、`Android.mk`ファイルを編集し、必要なファイルや設定を施した上で、`build_native.py`を実行します。
（ただ、Eclipse側でも実行時に自動的に上記ファイルを実行しているっぽいので、コマンドラインでやる必要はなさそうです）


## 「exception handling disabled, use -fexceptions to enable」というエラー

エラーをよく見てみるとJSONのパース用に追加した`picojson.h`で出ている模様。
どうやらexceptionの機能を有効にしないとならないみたい。（C++の新しい機能？）

色々探していたら[こちらの記事（Enable Exception C++ - Stack Overflow）](http://stackoverflow.com/questions/3217925/enable-exception-c)を見つけました。
一言で言うと「Android.mk」ファイル内に`LOCAL_CPP_FEATURES += exceptions`を追加しろ、とのこと。
とりあえず追加したことで、最初に出ていたエラーは解消出来ました。

```Android.mk
LOCAL_CPP_FEATURES += exceptions
```




## picojsonでエラーが出る

こちらの記事（[【cocos2dx】JSONを利用してサーバーと通信する。](http://albatrus.com/main/cocos2d/6237)）を参考にさせて頂きました。

## ローカルのJSONファイルで読み込み

`std::ifstream`だとなぜか読み込めなかったので、`deprecated`ではありますが少し前の記事を参考にファイル読み込みに対応しました。
*（iOSだと正常に動いていた）*

### 動かなかったコード

```cpp
std::string fullpath = FileUtils::getInstance()->fullPathForFilename("level.json");

// File open
std::ifstream inputStream;
inputStream.open(fullpath.c_str());
if (!inputStream.is_open()) {
    return;
}
std::stringstream sstream;
sstream << inputStream.rdbuf();
inputStream.close();

picojson::value v;
picojson::parse(v, sstream);

// 省略
```

### 動いたコード

```cpp
std::string fullpath = FileUtils::getInstance()->fullPathForFilename("level.json");
ssize_t fs = 0;
unsigned char *data = FileUtils::getInstance()->getFileData(fullpath, "r", &fs);

std::string str = (char*)data;

std::stringstream sstream(str);
picojson::value v;
picojson::parse(v, sstream);

// 省略
```

ファイルパスの指定はどちらも同じなので、`std::ifstream`の問題なのか・・。明確な理由は分かっていません( ;´Д`)



## extesionsをAndroidで動作させる

iOS向けにはファイルをインポートするだけですぐ使えたんですが、Androidの場合は若干設定をいじらないとならないみたいです。
[cocos2d-xでextensionsを含めてandroid向けにコンパイルする](http://qiita.com/AnzNetJp/items/57324cdc9ea57ec0ef0e)を参考に`Android.mk`ファイルを修正しました。

具体的には以下。

```Android.mk
# 追加
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_extension_static

# 追加
$(call import-module,extensions)
```

ただ、よくよく`Android.mk`を見てみるとextension用の記述はコメントアウトされていました。
他にもいくつかコメントアウトされており、必要に応じてコメントアウトを外せばすぐに使えるようになっています。


## JNIを使ってJavaとC++で連携する

今回、[Lobi SDK](https://developer.lobi.co/ja)を使う関係で、C++側からJavaのメソッドを叩く必要がありました。
C++からJavaメソッドを叩く場合は「[JNI（Java Native Interface）](http://ja.wikipedia.org/wiki/Java_Native_Interface)」を使います。

### ブリッジとなるcppファイルを作る

まずはブリッジとなるcppファイルを作成します。
`main.cpp`と同階層に新規でファイルを作成しました。

#### 実際のコード例

```LobiInterface.h
#include <jni.h>
namespace LobiInterface {
	void presentRanking();
	void sendRanking(const char *rankingId, const jlong score);
}
```

```LobiInterface.cpp
#include "LobiInterface.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"

#define CLASS_NAME "org.cocos2dx.cpp.AppActivity"
#define CALL_STATIC_VOID_METHOD(methodName,signature,...)     \
    JniMethodInfo t; \
    if (JniHelper::getStaticMethodInfo(t, CLASS_NAME, methodName, signature)) { \
        t.env->CallStaticVoidMethod(t.classID, t.methodID, __VA_ARGS__); \
        t.env->DeleteLocalRef(t.classID); \
    }

using namespace cocos2d;

void LobiInterface::presentRanking()
{
    CALL_STATIC_VOID_METHOD("presentRanking", "()V", NULL);
}

void LobiInterface::sendRanking(const char *rankingId, const jlong score)
{
    JniMethodInfo t;
    if (JniHelper::getStaticMethodInfo(t, CLASS_NAME, "sendRanking",
    		"("
    		"Ljava/lang/String;"
    		"J"
    		")V")) {
    	jstring jrankingId = t.env->NewStringUTF(rankingId);

        t.env->CallStaticVoidMethod(t.classID, t.methodID, jrankingId, score);
        t.env->DeleteLocalRef(t.classID);

        t.env->DeleteLocalRef(jrankingId);
        t.env->DeleteLocalRef(t.classID);
    }
}
```

JNIはJavaとC++とのブリッジを提供してくれるものです。
そのインターフェースとしてJava側で実装したメソッド名などを文字列で受け取り、それを実行します。
メソッドの引数もシグネチャとしてどういう型の引数が渡るかを指定しています。

### AppActivityにstaticメソッドを実装する

C++側の実装と対となるJava側の実装も必要です。
該当箇所を抜粋すると以下のような感じになります。

```java
public class AppActivity extends Cocos2dxActivity {
	
    // 中略
	
	public static void presentRanking() {
		LobiRanking.presentRanking();
	}
	
	public static void sendRanking(String rankingID, long score) {
		/* アカウントが作成済みかチェック */
		if(!LobiCore.isSignedIn()){
		    Log.d("LobiSDK", "アカウントが作成されていません");
		    return;
		}
		else {
		   Log.d("LobiRankingSDK", "Sending ranking score...");
		   
		   LobiRankingAPI.sendRanking(rankingID,
		                              score,
		                              new LobiCoreAPI.APICallback() {
		       @Override
		       public void onResult(int code, JSONObject response) {
		            if(code != LobiCoreAPI.APICallback.SUCCESS){
		            	Log.d("LobiRankingSDK", "Successed to send ranking score!");
		            }
		            else{
		            	Log.d("LobiRankingSDK", "Failed to send ranking score!");
		            }
		        }
		    });
		}
	}
}
```

#### Android.mkに読み込みファイルを追加する

最後にひとつだけハマったので書いておきます。
最初のほうに書いた、cppファイルを自動的に読み込む指定を書いておけばCocos2d-xでの実装は問題ありません。
が、今回のようにブリッジファイルを作った場合はそれもしっかりと`Android.mk`に追記しないとなりません。

自動化のコードを見ると`Classes`以下のcppファイルを列挙して読み込むようになっているので、それ以外の場所に追加したファイルは自動で追加されないのです。

意外と忘れがちなことだと思ったのでメモとして残しておきます。

-----------

## 作ったアプリたち

ちなみに今回のCocos2d-xで作ったアプリです。
社内のメンバーと競争で作ったものなので、それらも一緒に紹介します。
よかったら遊んでみてください( *'-')

### Swipe Defender

まずは自分から。
今回の「ゲームレース」で作ったゲームは「[Swipe Defender](https://itunes.apple.com/jp/app/swipedefender/id914301415?mt=8)」というゲームです。
スワイプでバリアを張って、隕石を打ち返すシンプルアクションゲームです。

[![sd-promotion-jp.png](https://qiita-image-store.s3.amazonaws.com/0/5616/793d27a8-342a-d054-bba4-ce6da779abf6.png "sd-promotion-jp.png")](https://itunes.apple.com/jp/app/swipedefender/id914301415?mt=8)

[![AppStoreBtn.png](https://qiita-image-store.s3.amazonaws.com/0/5616/87939e04-26e9-1f59-0791-e5a94ee7bae7.png "AppStoreBtn.png")](https://itunes.apple.com/jp/app/swipedefender/id914301415?mt=8)


[![ja_app_rgb_wo_60.png](https://qiita-image-store.s3.amazonaws.com/0/5616/82267b60-9369-9088-9d5b-cb06dce1c478.png "ja_app_rgb_wo_60.png")](https://play.google.com/store/apps/details?id=com.edo.swipedefender)

---------------------

### すしとぼく

[inon29](http://qiita.com/inon29)さんの作品「[すしとぼく](https://play.google.com/store/apps/details?id=com.inon29.game.sushiboku)」。シンプルながらかわいいイラストとポップな音が癖になります。

*Android版。（iOSはただいま申請中）*

[![an-promotion-jp.png](https://qiita-image-store.s3.amazonaws.com/0/5616/b266d8e1-5261-3455-5525-d53b92b3e6e7.png "an-promotion-jp.png")](https://play.google.com/store/apps/details?id=com.inon29.game.sushiboku)

[![ja_app_rgb_wo_60.png](https://qiita-image-store.s3.amazonaws.com/0/5616/82267b60-9369-9088-9d5b-cb06dce1c478.png "ja_app_rgb_wo_60.png")](https://play.google.com/store/apps/details?id=com.inon29.game.sushiboku)