cocos2d-xのゲーム製作に必ず必要というわけではないですが、JNI、Java Native Interfaceに関して記していきます。
・JNIについて
名前の通り、JavaとNative(C++)を繋ぐ為の仕組みです。
AndroidでNDKを使用し重い処理をC++側で行おうと思った時、JNIを介してやり取りをする事になるわけですが、cocos2d-xを利用する場合は逆にC++側からJavaのコードを呼び出すのに使用します。
ではそのC++側からJavaのコードを呼び出すというのを何に利用するのかですが、主に広告用だと思います。
広告も含まれますが、Viewに関する事はJava側で実装します。
C力検査ではおすすめアプリの表示に加え、レビュー依頼ダイアログの表示にも使用しています。
コード例は下記の様な感じになります。
- JniBridge.h -
#ifndef JNIBRIDGE_H_
#define JNIBRIDGE_H_
class JniBridge
{
public:
static void showAd();
};
#endif /* JNIBRIDGE_H_ */
- JniBridge.cpp -
#include "JniBridge.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"
#define MY_CLASS_NAME "org/cocos2dx/cpp/AppActivity"
USING_NS_CC;
void JniBridge::showAd()
{
JniMethodInfo methodInfo;
if (JniHelper::getStaticMethodInfo(methodInfo,MY_CLASS_NAME,"showAd","()V"))
{
methodInfo.env->CallStaticVoidMethod(methodInfo.classID,methodInfo.methodID);
methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
}
これはC++側のコードです。
これに加えて、Java側の呼び出されるメソッドが必要になりますが、とりあえずこちらのコードの解説を行います。
JniBridge.hは特に問題ないかと思います。
Publicの呼び出しようメソッドを宣言しているだけです。
JniBridge.cppではまず下準備として、JNIを利用する為にJniHelperをインクルードしています。
加えて、呼び出されるJava側のクラス名をMY_CLASS_NAMEとして「.」では無く「/」区切り文字列としてDefineしています。
showAdがJava側を呼び出すメソッドです。
MY_CLASS_NAME内にあるshowAdという引数無しのメソッドを呼び出します。
getStaticMethodInfoで呼び出しに必要な各種情報をJniMethodInfo へ格納し、CallStaticVoidMethodで呼び出しを行っています。
DeleteLocalRefでの解放処理まではセットで行います。
getStaticMethodInfoの引数は、
第1引数がJniMethodInfoを格納する宣言済みの変数
第2引数が対象クラス名
第3引数が対象メソッド名
第4引数が渡される引数の型
を指定します。
CallStaticVoidMethodの引数は、
第1引数、第2引数が固定でJniMethodInfo内のプロパティ
第3引数からが呼び出すメソッドに対して渡される引数
を指定します。
CallStaticVoidMethodのVoidは「戻り値」に対してかかっています。
「引数」がVoidかどうかはgetStaticMethodInfoの第4引数、"()V"で指定しています。
例えば、intを渡す場合は"(I)V"となり、CallStaticVoidMethodの第3引数に数値を指定すれば渡す事ができます。
引数を渡す例として、Java側にあるランキング登録用のメソッドを呼び出すには下記の様になります。
void JniBridge::setScore(int score)
{
JniMethodInfo methodInfo;
if (JniHelper::getStaticMethodInfo(methodInfo,MY_CLASS_NAME,"setScore","(I)V"))
{
methodInfo.env->CallStaticVoidMethod(methodInfo.classID,methodInfo.methodID,score);
methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
}
・Java側の実装
呼び出されるAppActivity.javaはインポートしたcocos2d-xプロジェクトに始めから存在します。
そこに広告表示用のメソッドを追加していきます。
- AppActivity.java(一部省略) -
public class AppActivity extends Cocos2dxActivity
{
private static Context sContext = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
sContext = this;
}
public static void showAd()
{
final Activity activity = (Activity)sContext;
activity.runOnUiThread(new Runnable()
{
public void run()
{
ViewGroup rootView = AppActivity.getRootView(activity);
if(rootView != null)
{
// rootViewに対して広告ViewをaddViewする
}
}
});
}
private static ViewGroup getRootView(Activity activity)
{
return (ViewGroup)activity.findViewById(android.R.id.content);
}
}
onCreate()でstaticのContextへthisを代入しています。
C++から呼び出すメソッドは全てstatic宣言されているメソッドになりますので、Contextの保持は必須です。
showAd()ではContextを取得し、runOnUiThreadでUIスレッドへ処理を渡しています。
これは、cocos2d-xのメインスレッドがUIスレッドではない為、そのままView等を操作すると例外が発生する為です。
必ずUIに関する操作はrunOnUiThreadのrun()内で処理する必要があります。
(通信だけ等UIが関わらない場合この限りではありません)
getRootViewではandroid.R.id.contentをfindViewして返しています。
android.R.id.contentはActivityのrootとなるViewです。後はここに広告用のViewをaddすれば良いだけです。
各ASPが配布するcocos2d-x用のモジュールも基本的にこれと同じ事をしているだけです。
中には安全の為同期処理を行っていたりする物もありますが、JNIでC++側からJavaを呼び出すという事を覚えておけばモジュール未配布の広告の実装も自前で行う事が出来ます。
これでcocos2d-xに関する記事投稿は終了です。
次回はC力検査製作時に試したASP、SSP、広告出稿に関して書きたいと思います。
・JNIについて
名前の通り、JavaとNative(C++)を繋ぐ為の仕組みです。
AndroidでNDKを使用し重い処理をC++側で行おうと思った時、JNIを介してやり取りをする事になるわけですが、cocos2d-xを利用する場合は逆にC++側からJavaのコードを呼び出すのに使用します。
ではそのC++側からJavaのコードを呼び出すというのを何に利用するのかですが、主に広告用だと思います。
広告も含まれますが、Viewに関する事はJava側で実装します。
C力検査ではおすすめアプリの表示に加え、レビュー依頼ダイアログの表示にも使用しています。
コード例は下記の様な感じになります。
- JniBridge.h -
#ifndef JNIBRIDGE_H_
#define JNIBRIDGE_H_
class JniBridge
{
public:
static void showAd();
};
#endif /* JNIBRIDGE_H_ */
- JniBridge.cpp -
#include "JniBridge.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"
#define MY_CLASS_NAME "org/cocos2dx/cpp/AppActivity"
USING_NS_CC;
void JniBridge::showAd()
{
JniMethodInfo methodInfo;
if (JniHelper::getStaticMethodInfo(methodInfo,MY_CLASS_NAME,"showAd","()V"))
{
methodInfo.env->CallStaticVoidMethod(methodInfo.classID,methodInfo.methodID);
methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
}
これはC++側のコードです。
これに加えて、Java側の呼び出されるメソッドが必要になりますが、とりあえずこちらのコードの解説を行います。
JniBridge.hは特に問題ないかと思います。
Publicの呼び出しようメソッドを宣言しているだけです。
JniBridge.cppではまず下準備として、JNIを利用する為にJniHelperをインクルードしています。
加えて、呼び出されるJava側のクラス名をMY_CLASS_NAMEとして「.」では無く「/」区切り文字列としてDefineしています。
showAdがJava側を呼び出すメソッドです。
MY_CLASS_NAME内にあるshowAdという引数無しのメソッドを呼び出します。
getStaticMethodInfoで呼び出しに必要な各種情報をJniMethodInfo へ格納し、CallStaticVoidMethodで呼び出しを行っています。
DeleteLocalRefでの解放処理まではセットで行います。
getStaticMethodInfoの引数は、
第1引数がJniMethodInfoを格納する宣言済みの変数
第2引数が対象クラス名
第3引数が対象メソッド名
第4引数が渡される引数の型
を指定します。
CallStaticVoidMethodの引数は、
第1引数、第2引数が固定でJniMethodInfo内のプロパティ
第3引数からが呼び出すメソッドに対して渡される引数
を指定します。
CallStaticVoidMethodのVoidは「戻り値」に対してかかっています。
「引数」がVoidかどうかはgetStaticMethodInfoの第4引数、"()V"で指定しています。
例えば、intを渡す場合は"(I)V"となり、CallStaticVoidMethodの第3引数に数値を指定すれば渡す事ができます。
引数を渡す例として、Java側にあるランキング登録用のメソッドを呼び出すには下記の様になります。
void JniBridge::setScore(int score)
{
JniMethodInfo methodInfo;
if (JniHelper::getStaticMethodInfo(methodInfo,MY_CLASS_NAME,"setScore","(I)V"))
{
methodInfo.env->CallStaticVoidMethod(methodInfo.classID,methodInfo.methodID,score);
methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
}
・Java側の実装
呼び出されるAppActivity.javaはインポートしたcocos2d-xプロジェクトに始めから存在します。
そこに広告表示用のメソッドを追加していきます。
- AppActivity.java(一部省略) -
public class AppActivity extends Cocos2dxActivity
{
private static Context sContext = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
sContext = this;
}
public static void showAd()
{
final Activity activity = (Activity)sContext;
activity.runOnUiThread(new Runnable()
{
public void run()
{
ViewGroup rootView = AppActivity.getRootView(activity);
if(rootView != null)
{
// rootViewに対して広告ViewをaddViewする
}
}
});
}
private static ViewGroup getRootView(Activity activity)
{
return (ViewGroup)activity.findViewById(android.R.id.content);
}
}
onCreate()でstaticのContextへthisを代入しています。
C++から呼び出すメソッドは全てstatic宣言されているメソッドになりますので、Contextの保持は必須です。
showAd()ではContextを取得し、runOnUiThreadでUIスレッドへ処理を渡しています。
これは、cocos2d-xのメインスレッドがUIスレッドではない為、そのままView等を操作すると例外が発生する為です。
必ずUIに関する操作はrunOnUiThreadのrun()内で処理する必要があります。
(通信だけ等UIが関わらない場合この限りではありません)
getRootViewではandroid.R.id.contentをfindViewして返しています。
android.R.id.contentはActivityのrootとなるViewです。後はここに広告用のViewをaddすれば良いだけです。
各ASPが配布するcocos2d-x用のモジュールも基本的にこれと同じ事をしているだけです。
中には安全の為同期処理を行っていたりする物もありますが、JNIでC++側からJavaを呼び出すという事を覚えておけばモジュール未配布の広告の実装も自前で行う事が出来ます。
これでcocos2d-xに関する記事投稿は終了です。
次回はC力検査製作時に試したASP、SSP、広告出稿に関して書きたいと思います。