まずはメインループ ― 2013/11/02
前回、テーマを「Android NDKのプログラミング」としましたが、これではちょっと曖昧なのでとりあえず目標を「2Dゲームの作成」として、必要なものを順番に作っていくことにします。
そこで一番初めの今回は、
・初期化
・一定間隔でメインの処理
という全体の流れ(だけ)を作成します。
それでは、なにはなくともアクティビティ
BaseActivity.java
アクティビティのonCreateでサーフェスビューとレンダラーを作成するとか、System.loadLibraryでnative側のプログラムを読み込むとかいろいろありますが、この辺はNDKのプロラムでは共通事項なので説明は省きます。シンプルに作っているので、見れば大体わかると思います。
さて、プログラムの流れを追っていくと、
BaseActivityの onCreate → onResume
BaseViewの onResume
BaseRendererの onSurfaceCreated → onSurfaceChanged
と進みます。
ここでまず、nativeの関数 initNativeを呼んでnative側を初期化しておきます。
次に、「一定間隔でメインの処理」を行うための設定をします。
ScheduledExecutorServiceのメソッド
ScheduledFuture<?> scheduleAtFixedRate(
Runnable command,
long initialDelay,
long period,
TimeUnit unit)
時間単位 unitで、initialDelay後から delay毎にタスク commandを実行します。
このプログラムでは、
・1000/30ms毎に
・surface_view.requestRender()を実行する
となります。
BaseViewのコンストラクタで setRenderMode(RENDERMODE_WHEN_DIRTY)としているので、surface_viewの requestRender()により rendererの onDrawFrame()が呼ばれます。
よって、1000/ms毎に、nativeの関数 updateNativeが呼ばれることになります。
それでは、native側のプログラム
native.cpp
今回はログを表示するだけで、何もしません。
なお、初期化処理といいつつ initNativeは起動時だけではなくサスペンドからのレジューム時などにも呼ばれますので注意してください。
プロジェクト一式は、こちら。
そこで一番初めの今回は、
・初期化
・一定間隔でメインの処理
という全体の流れ(だけ)を作成します。
それでは、なにはなくともアクティビティ
BaseActivity.java
package sys; import android.app.Activity; import android.content.Context; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.util.Log; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.opengles.GL10; /******************** アクティビティ ********************/ public class BaseActivity extends Activity { static { System.loadLibrary("native"); } private BaseView surface_view; // ビュー private BaseRenderer renderer; // レンダラー private ScheduledExecutorService executor; // 定期実行管理 private ScheduledFuture<?> future; public native void initNative(int _w, int _h); // native部初期化 public native boolean updateNative(); // native部稼働 /********** 開始 **********/ @Override protected void onCreate(Bundle _savedInstanceState) { super.onCreate(_savedInstanceState); renderer = new BaseRenderer(); // レンダラー作成 surface_view = new BaseView(getApplication(), renderer); // ビュー作成 setContentView(surface_view); executor = Executors.newSingleThreadScheduledExecutor(); // 定期実行管理 } /********** 終了 **********/ @Override protected void onDestroy() { super.onDestroy(); executor.shutdown(); } /************** 一時停止 **************/ @Override protected void onPause() { super.onPause(); if ( future != null ) { // 定期実行停止 future.cancel(false); future = null; } surface_view.onPause(); } /********** 再開 **********/ @Override protected void onResume() { super.onResume(); surface_view.onResume(); } /************ ビュー ************/ class BaseView extends GLSurfaceView { public BaseView(Context _context, BaseRenderer _renderer) { super(_context); setEGLContextClientVersion(2); // OpenGL ES 2.0使用 setEGLConfigChooser(new ConfigChooser()); setRenderer(_renderer); setRenderMode(RENDERMODE_WHEN_DIRTY); } class ConfigChooser implements GLSurfaceView.EGLConfigChooser { @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { final int[] configAttribs = { EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_ALPHA_SIZE, 8, EGL10.EGL_NONE }; int[] num_config = new int[1]; egl.eglChooseConfig(display, configAttribs, null, 0, num_config); EGLConfig[] configs = new EGLConfig[num_config[0]]; egl.eglChooseConfig(display, configAttribs, configs, num_config[0], num_config); return configs[0]; } } } /**************** レンダラー ****************/ class BaseRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 _gl, EGLConfig _config) {} public void onSurfaceChanged(GL10 gl, int width, int height) { initNative(width, height); // native部初期化 if ( future == null ) { // 定期実行開始 future = executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { surface_view.requestRender(); // 描画リクエスト } }, 0, 1000/30, TimeUnit.MILLISECONDS); } } public void onDrawFrame(GL10 gl) { if ( !updateNative() ) { // native部稼働 finish(); } } } } /***************** End of File ***************************************************/
アクティビティのonCreateでサーフェスビューとレンダラーを作成するとか、System.loadLibraryでnative側のプログラムを読み込むとかいろいろありますが、この辺はNDKのプロラムでは共通事項なので説明は省きます。シンプルに作っているので、見れば大体わかると思います。
さて、プログラムの流れを追っていくと、
BaseActivityの onCreate → onResume
BaseViewの onResume
BaseRendererの onSurfaceCreated → onSurfaceChanged
と進みます。
ここでまず、nativeの関数 initNativeを呼んでnative側を初期化しておきます。
次に、「一定間隔でメインの処理」を行うための設定をします。
ScheduledExecutorServiceのメソッド
ScheduledFuture<?> scheduleAtFixedRate(
Runnable command,
long initialDelay,
long period,
TimeUnit unit)
時間単位 unitで、initialDelay後から delay毎にタスク commandを実行します。
このプログラムでは、
・1000/30ms毎に
・surface_view.requestRender()を実行する
となります。
BaseViewのコンストラクタで setRenderMode(RENDERMODE_WHEN_DIRTY)としているので、surface_viewの requestRender()により rendererの onDrawFrame()が呼ばれます。
よって、1000/ms毎に、nativeの関数 updateNativeが呼ばれることになります。
それでは、native側のプログラム
native.cpp
#include <jni.h> #include <android/log.h> static int cnt = 0; #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "INFO", __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "ERROR", __VA_ARGS__) extern "C" { JNIEXPORT void JNICALL Java_sys_BaseActivity_initNative(JNIEnv*, jobject, jint, jint); JNIEXPORT jboolean JNICALL Java_sys_BaseActivity_updateNative(JNIEnv*, jobject); } /************ 初期化 ************/ JNIEXPORT void JNICALL Java_sys_BaseActivity_initNative(JNIEnv*, jobject, jint _width, jint _height) { LOGI("initNative (%d, %d)", _width, _height); } /********** 稼働 **********/ JNIEXPORT jboolean JNICALL Java_sys_BaseActivity_updateNative(JNIEnv*, jobject) { if ( cnt % 60 == 0 ) { LOGI("updateNative %4d", cnt/60); } cnt++; return JNI_TRUE; } /**************** End of File *************************************************/
今回はログを表示するだけで、何もしません。
なお、初期化処理といいつつ initNativeは起動時だけではなくサスペンドからのレジューム時などにも呼ばれますので注意してください。
プロジェクト一式は、こちら。
最近のコメント