前提
- cocos2d-xのディレクトリを汚さずにiOS/Androidで使えるPluginを作る
- メソッドの引数にURLを指定すると、そのURLを外部ブラウザで開くPluginを作る
- JNIの解説はしない
- 細かいこと気にしない(とりあえず動かすのを目標にする)
- 作業時のcocos2d-xのversionは3.1.1
- version依存のところは無いと思うけど、一応記載
Plugin入れるところ
Android.mk書いて、パス通せば何処に入れても良いんだけど、今回はcocosコマンドで作ったプロジェクトにlibs/
を作って、そこにPluginを入れる。
. ├── CMakeLists.txt ├── Classes ├── Resources ├── cocos2d ├── libs // <- ここに入れる ├── proj.android └── proj.ios_mac
フォルダ作る
libs/にsample_plugin
ってフォルダを作って、そこで作業する。
iOSの場合、Xcodeに追加するのはsample_plugin/sample.h
とsample_plugin/ios/
以下。
sample_plugin ├── sample.h // 共通のコードは`sample_plugin/`に直接置く ├── android // Android用のcpp/jni/javaのコード │ ├── Android.mk │ ├── sampleAndroid.cpp │ ├── java │ └── jni └── ios // iOS用のコード └── sampleIOS.mm
共通のinterfaceを宣言する
sample.hに以下の宣言をする。Sample::func()
を実現するのに、Android/iOSのネイティブの機能を呼び出したいという感じ。
#ifndef __Sample_H_ #define __Sample_H_ class Sample { public: static void func(std::string text); }; #endif //__Sample_H_
iOSの実装をする
sample_plugin/ios/sampleIOS.mm
にiOS用の実装を書いていく。ファイルの命名は、cocos2d-xに準じた。
Objective-C++なので、C++のメソッドの中でUIApplicationなどのクラスを使える。 iOSについては、全く難しいところが無いのでさらっと終わる。
#include "sample.h" void ShareText::func(std::string text) { [[UIApplication sharedApplication] openURL:@(text.c_str())]; }
Androidの実装
cocos2d-xからAndroidネイティブコード呼び出すのには、コード書くのだけで上記作業が必要。今回は上から順に作業していくけど、別の所にAndroidのプロジェクトを作って、そこでJavaだけ書いてから、C++と繋いだほうがスムーズに行くと思う。
上記とは別に、書いたコードをcocos2d-xで利用するために、以下の作業が必要。
- Plugin用のAndroid.mkを書く
- Plugin用のandroidProject作る
proj.android/jni/Android.mk
に作ったPluginを追加する- proj.androidにPluginのクラスパスを追加する
大変だった。
cocos2d-xから呼び出すC++クラスの実装
C++のコードからは、JNIのメソッドを呼び出すだけにした。 具体的な実装は、極力funcJNIから呼び出されるjavaのコードで行う。
#include "sample.h" #include "jni/sampleJni.h" void ShareText::func(std::string text) { funcJNI(text); }
C++からJavaを呼び出すためのJNI書く
sampleJni.h
Cで書いた。調べた感じ、cppでも書けるっぽいけど、cocos2d-xのJNIコードはCで書かれてたので、それを参考にした
#ifndef __Java_org_Sample_H_ #define __Java_org_Sample_H_ extern "C" { void funcJNI(std::string url); } #endif //__Java_org_Sample_H_
sampleJni.cpp
cocos2d::JniHelper
がJNI使うの少し楽にしてくれてるっぽい。
package me.gin0606
にSampleHelper
というクラスを作成して、その中のstaticメソッドを呼び出す事にしている。SampleHelper.javaはあとで書く。
#include "jni/JniHelper.h" #include <string.h> #include <jni.h> // packageの`.`を`/`にしたものを使う。 #define kSampleHelper "me/gin0606/SampleHelper" using namespace cocos2d; extern "C" { void funcJNI(std::string url) { JniMethodInfo t; if (JniHelper::getStaticMethodInfo(t, kShareTextHelper, "funcJava", "(Ljava/lang/String;)V")) { jstring jUrl = t.env->NewStringUTF(url.c_str()); t.env->CallStaticVoidMethod(t.classID, t.methodID, jUrl); t.env->DeleteLocalRef(jUrl); t.env->DeleteLocalRef(t.classID); } } }
PluginのAndroid.mkを書く
Java呼び出す前に、一旦funcJNI
の中身を空にして、先にAndroid.mkを書いてしまう。
これを書いて、cocos2d-xからSample::funcを呼び出せば、funcJNI
が呼ばれるようになる。わかりやすいようにLog仕込んでもいいと思う。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := sample_static LOCAL_MODULE_FILENAME := libsample LOCAL_SRC_FILES := \ sampleAndroid.cpp \ jni/sampleJni.cpp LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/.. LOCAL_C_INCLUDES := $(LOCAL_PATH)/../ \ $(LOCAL_PATH)/ \ $(LOCAL_PATH)/jni include $(BUILD_STATIC_LIBRARY) $(call import-module,.)
JNIから呼ばれるJava書く
funcJNI
を元に戻して、Javaを書く。
Androidプロジェクト作成
この作業をやらなくても、proj.android/src
やcocos2d/cocos/platform/android/java/src
にJavaのファイルを置いてしまってもいいが、「cocos2d-xのディレクトリを汚さず」という前提があるので、lib-projectを作る。
cd libs/sample_plugin/android/java android create lib-project --target android-14 --package me.gin0606 --path ./
これを実行すると、AndroidManifest.xml
などのファイルが生成される。
パス設定
作ったProjectから、cocos2d-xのパッケージにアクセス出来るようにする。
cd libs/sample_plugin/android/java android update project -p . -l ../../../../cocos2d/cocos/platform/android/java
これでimport org.cocos2dx.lib.Cocos2dxActivity;
とか出来るようになった。
Java書く
libs/sample_plugin/android/java/src/me/gin0606/
にSampleHelper.java
を作って、Java書く。
cocos2d-xのAndroidアプリの大本のCocos2dxActivityがgetContextでアプリのContext(実体はActivity)を何処からでも取れるので、ネイティブコード書きやすいと思う。
package me.gin0606; import org.cocos2dx.lib.Cocos2dxActivity; import android.content.Intent; import android.net.Uri; import android.app.Activity; public class SampleHelper { public static void funcJava(String url){ Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); try { ((Activity)Cocos2dxActivity.getContext()).startActivityForResult(intent, 0); } catch (Exception e) { } } }
アプリに作ったPluginを含める
cd proj.android android update project -p . -l ../libs/sample_plugin/android/java
これを実行すると、proj.android/project.properties
に差分が出る。android.library.reference.1
がcocos2dのjavaで、android.library.reference.2
が作ったjavaになるはず。
アプリ起動する
あとは任意の方法でiOS/AndroidのアプリをBuild&Runすればいい。
終わり。
参考
- http://www.ne.jp/asahi/hishidama/home/tech/java/jni_code.html
- なんか頑張れば全くJava書かずにネイティブの機能呼び出せそうな気がしたけど苦行っぽい