まずはメインループ ― 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は起動時だけではなくサスペンドからのレジューム時などにも呼ばれますので注意してください。
プロジェクト一式は、こちら。
コメント
トラックバック
このエントリのトラックバックURL: http://raseene.asablo.jp/blog/2013/11/02/7035559/tb
コメントをどうぞ
※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。
※投稿には管理者が設定した質問に答える必要があります。