読者です 読者をやめる 読者になる 読者になる

ほげほげ(仮)

仮死状態

AndroidからCocos2d-xの処理を呼び出す

Cocos2d-x Android

AndroidJavaからCocos2d-xのC++の処理を呼び出します。

Objective-Cに比べるとかなり面倒です。

あとで出てくるjavahの箇所は面倒なので、関数名の規則がわかるなら飛ばしても平気です。

関連

環境

  • Cocos2d-x 3.2

JavaからC++を呼び出す処理

public class AppActivity extends Cocos2dxActivity {

    @Override
    protected void onPause() {
        super.onPause();
        execute("onPause");
    }

    // C++関数
    public static native void executeCpp(String string);

    public static void execute(String string) {
        executeCpp(string);
    }

}

native修飾子でC++側の関数を定義します。Java側からはこれを呼び出すだけです。

今回は手っ取り早く試すためにonPauseの時にC++の処理を呼んでいます。本来はもっと有意義な箇所で使いましょう。

補足

System.loadLibrary("cocos2dcpp");

通常のJNIであれば上記のコードでsoファイルを読み込む必要がありますが、Cocos2d-xの場合は不要です。

Cocos2d-xはプロジェクトを作った際に最初に起動するActivityがCocos2dxActivityを継承していて、そっちで読み込みはやってくれていますので、特に不要です。Cocos2dxActivityのコードを見ればすぐわかると思います。

C++ヘッダー

JNIから呼び出されるC++の関数は規則があって、Java_パッケージ名_クラス名_関数名で作成しなければなりません。

org_cocos2dx_cpp_AppActivity.hというファイルをClasses/platform/android/jniに配置して次のようにします。

#include <jni.h>

#ifndef _Included_org_cocos2dx_cpp_AppActivity
#define _Included_org_cocos2dx_cpp_AppActivity

extern "C" {

JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_executeCpp
  (JNIEnv *, jclass, jstring);

}

#endif

javah

※ 関数名の規則が分かるならここはスキップしても大丈夫です。結構面倒なので。

手動で作成しても良いですがタイポとかで面倒にならないよう、javahコマンドでヘッダーを自動生成する方法です。

次のコマンドでヘッダーファイルが生成されます。

カレントディレクトリはproj.androidで実行してください。

$ javah -classpath "bin/classes:$ANDROID_SDK_ROOT/platforms/android-10/android.jar" -d ../Classes/platform/android/jni org.cocos2dx.cpp.AppActivity

毎回これは面倒なのでantで実行できるようにします。

javahをant実行

カレントディレクトリはproj.androidで作業します。

  • local.properties作成

antでのAndroidSDKの場所を指定するために必要になります。

次のコマンドで自動で作成されます。

※ proguard-project.txtも上書きされてしまうので、設定済みの場合は一旦退避させておいてください。

$ android update project -p .
  • antファイル作成

eclipseのプロジェクト直下(build.xmlの同じ箇所)と同じ箇所にcustom_rules.xmlというファイルを作って次のようにします。

destdirにて出力先を指定します。出力先は事前にフォルダを作成しておいてください。

classタグでヘッダーを出力したいクラスを指定します。複数ある場合はclassタグを複数かけば良いです。

<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules" >
    <target name="javah" >
        <javah destdir="../Classes/platform/android/jni/" >
            <classpath>
                <pathelement path="${sdk.dir}/platforms/${target}/android.jar" />
                <pathelement path="./bin/classes" />
            </classpath>
            <class name="org.cocos2dx.cpp.AppActivity" />
        </javah>
    </target>
</project>
  • ant実行
$ ant javah

これでヘッダーファイルが作成されます。

C++実装

javahで作成したヘッダーファイルと同じ箇所に実装ファイルを作成します。今回はorg_cocos2dx_cpp_AppActivity.cppとなります。

関数名はjavahで生成した箇所からコピーすると楽です。(引数に変数名が無いので、それは適宜追記してください)

#include "org_cocos2dx_cpp_AppActivity.h"

#include "cocos2d.h"

JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_executeCpp(JNIEnv *env, jclass, jstring jarg)
{
    auto arg = env->GetStringUTFChars(jarg, nullptr);
    std::string string = arg;
    env->ReleaseStringUTFChars(jarg, arg);
    cocos2d::log("%s", string.c_str());
}

今回は単純にログを出してるだけです。

実行

あとは実行すれば、JavaからC++を呼び出せるはずです。

今回のサンプルではHOMEキーを押すとonPauseが呼ばれて、そこからC++側の処理が実行されログが出力されます。

ディレクトリ構成

出来上がったディレクトリ構成はこんな感じです。

Cocos2d-xからAndroidの呼び出しと、iOSの対応も含めての構成です。

Classes
├── AppDelegate.cpp
├── AppDelegate.h
├── HelloWorldScene.cpp
├── HelloWorldScene.h
└── platform
    ├── NativeBridge.h
    ├── android
    │   ├── NativeBridge.cpp
    │   └── jni
    │       ├── NativeControllerJni.cpp
    │       ├── NativeControllerJni.h
    │       ├── org_cocos2dx_cpp_AppActivity.cpp
    │       └── org_cocos2dx_cpp_AppActivity.h
    └── ios
        ├── NativeBridge.mm
        └── objc
            ├── NativeController_objc.h
            └── NativeController_objc.mm