まず最初にiOSで作り始め、ある程度完成した段階でAndroidへ持って行こうとしたときにハマったのでそのメモです。
色々一筋縄ではいかない様子。
Android.mkを編集する
Cocos2d-xが最初に生成するAndroid Projectはある程度準備されています。
その中でも、Android.mk
は「Makefile」で、セットアップに使われます。
Android.mk
に設定されているのはデフォルトのものなので、作成した当初のファイル名(HelloWorld
など)が残ったままになっています。
なので、まずはLOCAL_SRC_FILES
と書かれている部分を、実際に読み込ませたいファイル一覧に書き換えます。
LOCAL_SRC_FILES := swipedefendercpp/main.cpp
LOCAL_SRC_FILES += $(CPP_FILES:$(LOCAL_PATH)/%=%)
ちなみに2行目の部分は、以下の記事を参考に、作成したcppファイルを自動的に読み込む設定を行っています。
必要なC++ファイルを自動的に読み込ませる
こちらの記事(Cocos2d-xで新しく作ったクラスを毎回Android.mkに追加せずに済ます)を参考にさせて頂きました。
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)を見つけました。
一言で言うと「Android.mk」ファイル内にLOCAL_CPP_FEATURES += exceptions
を追加しろ、とのこと。
とりあえず追加したことで、最初に出ていたエラーは解消出来ました。
LOCAL_CPP_FEATURES += exceptions
picojsonでエラーが出る
こちらの記事(【cocos2dx】JSONを利用してサーバーと通信する。)を参考にさせて頂きました。
ローカルのJSONファイルで読み込み
std::ifstream
だとなぜか読み込めなかったので、deprecated
ではありますが少し前の記事を参考にファイル読み込みに対応しました。
(iOSだと正常に動いていた)
動かなかったコード
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);
// 省略
動いたコード
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向けにコンパイルするを参考にAndroid.mk
ファイルを修正しました。
具体的には以下。
# 追加
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_extension_static
# 追加
$(call import-module,extensions)
ただ、よくよくAndroid.mk
を見てみるとextension用の記述はコメントアウトされていました。
他にもいくつかコメントアウトされており、必要に応じてコメントアウトを外せばすぐに使えるようになっています。
JNIを使ってJavaとC++で連携する
今回、Lobi SDKを使う関係で、C++側からJavaのメソッドを叩く必要がありました。
C++からJavaメソッドを叩く場合は「JNI(Java Native Interface)」を使います。
ブリッジとなるcppファイルを作る
まずはブリッジとなるcppファイルを作成します。
main.cpp
と同階層に新規でファイルを作成しました。
実際のコード例
#include <jni.h>
namespace LobiInterface {
void presentRanking();
void sendRanking(const char *rankingId, const jlong score);
}
#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側の実装も必要です。
該当箇所を抜粋すると以下のような感じになります。
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」というゲームです。
スワイプでバリアを張って、隕石を打ち返すシンプルアクションゲームです。
すしとぼく
inon29さんの作品「すしとぼく」。シンプルながらかわいいイラストとポップな音が癖になります。
Android版。(iOSはただいま申請中)