【Android】OpenGL ES入門。スクリーン座標をワールド座標に変換してタッチ位置にキューブ弾4【終】

touch4ShootTop

はじめに


ついに今回で座標変換とベクトルの話がラストです。

今まで1~3までの記事は以下を参照してください。

【Android】OpenGL ES入門。スクリーン座標をワールド座標に変換してタッチ位置にキューブ弾

【Android】OpenGL ES入門。スクリーン座標をワールド座標に変換してタッチ位置にキューブ弾2

【Android】OpenGL ES入門。スクリーン座標をワールド座標に変換してタッチ位置にキューブ弾3

プロジェクトファイルの置き場であるGitさんのURLは以下です。

gitPurobana

https://github.com/rashu404/purobana

まずはおさらい


記事1回目から何をやってきたのかおさらいです。

キューブ弾を発射するために、まずどの方向へ行くのかを決めてあげる「ベクトル」が必要になります。

そしてカメラ位置に生成したキューブに、画面奥へのベクトルを与え、弾道を作ればいいわけです。

さて、これをどうやって作ろうか?

答えは今わかっているベクトルをフル活用して、足し算、引き算、掛け算をし、最後にデバイスに合わせればいいですね。

・・・と、こんな感じ。

後半のコードと解説


後半部分のコードはこうなっています。

Vector3f rayToCenter = new Vector3f();
rayToCenter.add(rayFrom, rayForward);
Vector3f dHor = new Vector3f(hor);
dHor.scale(1f / width);
Vector3f dVert = new Vector3f(vertical);
dVert.scale(1.f / (float) height);

Vector3f tmp1 = new Vector3f();
Vector3f tmp2 = new Vector3f();
tmp1.scale(0.5f, hor);
tmp2.scale(0.5f, vertical);

Vector3f rayTo = new Vector3f();
rayTo.sub(rayToCenter, tmp1);

rayTo.add(tmp2);

tmp1.scale(x, dHor);
tmp2.scale(y, dVert);

rayTo.add(tmp1);
rayTo.sub(tmp2);
return rayTo;

World#getRay()を参照してください。

touch4ShootDVertHor

Vector3f rayToCenter = new Vector3f();
rayToCenter.add(rayFrom, rayForward);
Vector3f dHor = new Vector3f(hor);
dHor.scale(1f / width);
Vector3f dVert = new Vector3f(vertical);
dVert.scale(1.f / (float) height);

Vector3f型でrayToCenterを定義し、カメラの位置であるrayFromとlookAt – eyeを引いたrayForwardのベクトルを足し算します。

その和は、eyeの最初からrayForwardの末尾までの大きさになります。

さらにHor(Horizon)とVerticalをデバイスのスクリーンの大きさに合わせます。

1/widthを掛け算するので、dHor ÷ widthとも言いかえることができます。

Vector3f tmp1 = new Vector3f();
Vector3f tmp2 = new Vector3f();
tmp1.scale(0.5f, hor);
tmp2.scale(0.5f, vertical);

tmp1とtmp2を定義します。

さらにZ=10000のFOVの端から端までキューブを吹き飛ばせるhorとverticalを半分にします。

touch4ShootTmp1

Vector3f rayTo = new Vector3f();
rayTo.sub(rayToCenter, tmp1);

rayTo.add(tmp2);

touch4ShootTmp2rayTo

rayToとtmpを足し引きすると、2つ目の記事で出て来た、

touch2ShootVecXYmax

これの出来上がりです。

あとは今まで長らく時間をかけて説明した和差・スケーリングを繰り返せば、できあがりです。

tmp1.scale(x, dHor);
tmp2.scale(y, dVert);

rayTo.add(tmp1);
rayTo.sub(tmp2);
return rayTo;

touchShootTop

 

最後に


ずいぶんと長くかかりましたが、タッチでワールドの位置を指定するのはかなりベクトルの理解が必要になってきます。

これだけで短めの本書けるレベルです(笑)

グーグル先生の順位が上がって来たなーやったなーとか心の隅で自己満足を覚えつつ、何だかんだいつも見てくださっている方々に感謝感謝です。