算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

つくって学ぶ!OpenGLはじめの一歩【補足】

昨日ABCで講演しました。発表資料はこちらでダウンロードできます。

長い講演は初めてで緊張しました。みなさんに興味を持っていただこうと、講演内容を盛りすぎたこと、そして理解しやすいようにとライブコーディングを試みたこと、これが裏目に出て時間が足らず、最後は駆け足での説明になってしまいました。なので、補足という形でここでお話ししたいと思います。

縦横比一定化
GLSurfaceView.Rendererの実装クラスで次のように記述して縦横の比を一定にする工夫を施します。

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    // 縦長前提
    int h = (int) (width * this.heightRatio / this.widthRatio);
    gl.glViewport(0, (height - h) / 2, width, h);

    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, 0.5f, -0.5f);

    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();
}

heightRatio, widthRatioは縦横の比を表すフィールドです。例えば、heightRatioに3, widthRatioに2が代入されているとすると、横 : 縦 = 2 : 3であることを意味します。
また、GL10#glOrthof()で座標系を設定していますが、引数を見ればわかりますが、幅が2.0, 高さが3.0となっており、幅 : 高さ = 2: 3 となっていて丁度良いのです!(雑な説明で申し訳ないです><)

タッチイベントへの対応
画面をタッチした時にMotionEvent#getX()などで返される値は、上で設定した座標系には対応していません。なので、上手いこと変換してやります。
例えば、タッチしたX座標が画面幅の50%だった場合(つまり画面幅に対して中央をタッチしたとき)、X座標を 0.0 に変換します。例えば、一番右端をタッチした場合、X座標を 1.0 に変換します。
GLSurfaceViewのサブクラスの、onTouchEvent()内のコードはこんな感じです。

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (this.onTouchListener == null) {
        return super.onTouchEvent(event);
    }

    final float width = this.getWidth();
    final float height = this.getHeight();
    final float widthRatio = this.renderer.getWidthRatio();
    final float heightRatio = this.renderer.getHeightRatio();

    final float x = event.getX() / width * widthRatio - widthRatio / 2.0f;
    final float y = event.getY() / height * -heightRatio + heightRatio / 2.0f;

    return this.onTouchListener.onTouch(this, event, x, y);
}

this.onTouchListenerはView.OnTouchListenerインタフェースを継承したインタフェースです。タッチした座標と画面のサイズ、描画領域の縦横比を用いて、新しい座標の値を計算しています。

ウィジェットの配置
GLSurfaceViewはActivityでセットしますよね。

this.setContentView(glSurfaceView);

こんな感じで。
さらにこの上にViewをセットできます。Activity#addContentView()を使用します。具体的には次のように記述します。

View view = this.getLayoutInflater().inflate(R.layout.main, null);
this.addContentView(view, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

上記1行目でレイアウトファイルからViewを生成します。次の行でViewを画面いっぱいに配置しています。
ウィジェットの配置については、また機会があったら詳細に書きます。

詳しくはOpenGLで作る Android SDKゲームプログラミングを参照してください。
また、東京近辺のJAG関係のイベントには積極的に参加しますので、そちらでお会いしたときに質問していただければ、できる限りお答えしたいと思います。よちよちAndroidサンデープログラミングの会の勉強会にはほぼ毎週参加しているので、こちらもよかったらどうぞ!