今回のサンプルではマウスカーソルの位置にあるモデルを選択できるようにしています。カーソルをモデルに合わせると、Hit テキストが True に変化します。
スクリーン座標から3次元空間座標へ変換
マウスなどで3次元空間上のモデルなどを選択したい場合があると思います。この場合は、スクリーン上の2次元座標点からモデルの存在する3次元座標に変換してあたり判定などを行う必要があります。
しかし、2次元から3次元に要素を拡張するため、X,Y のみの2次元のスクリーン座標から、3次元座標の点を求めることはできません。たとえば、実際に画面をクリックするイメージを考えてもらうとわかるかと思いますが、クリックしたときの3次元空間の位置が、オブジェクトよりも手前の位置なのかオブジェクト自体なのか、はたまたオブジェクトの奥なのかを判定することはできません。
そのため、クリックした位置は点で表すのではなく、カメラの位置からクリックした方向に伸ばした線として扱います。その線とオブジェクトとの衝突判定を行うことによってモデルの選択などができるようになります。ちなみに線のパラメータは XNA では Ray という構造体で扱うことができます。
スクリーンの位置から3次元空間の位置を取得
XNA にはスクリーンでクリックした方向へ向かうような線を求めるようなメソッドなどはありません。しかし、スクリーン座標と奥行きを指定することで3次元空間の点を求めることができるので、カメラの位置と特定の深度で変換した3次元空間座標点を結ぶことによって線を求めることができます。
スクリーン空間座標からオブジェクト空間座標を求めるには「Viewport.Unproject」メソッドを使用することで簡単に求めることができます。
Viewport viewport = this.GraphicsDevice.Viewport;
Vector3 screenPosition = new Vector3(this.markPosition, 1.0f);
Vector3 worldPoint = viewport.Unproject(screenPosition,
this.projection,
this.view,
Matrix.Identity);
第1引数にはスクリーン座標に深度を含めた Vector3 を渡します。X, Y にはスクリーンの座標を設定し、Z に深度の値を設定します。深度はプロジェクションマトリックスの「nearPlaneDistance」と「farPlaneDistance」のパラメータに依存し、0.0f を指定するとカメラの位置から nearPlaneDistance の距離、1.0f を指定するとカメラの位置から farPlaneDistance の距離を求めることができます。
第2引数にはプロジェクションマトリックス、第3引数にはビューマトリックスを指定します。
戻り値としてオブジェクト空間ベクトルを求めることができます。
レイの作成
線のパラメータには Ray 構造体が使用できます。コンストラクタの第1引数にレイの開始点を指定し、第2引数にレイの向きを指定します。
開始点にカメラの位置を設定し、向きに変換済みの3次元空間座標からカメラの位置を引いて向きを算出します。向きは Vector3.Normalize メソッドで単位ベクトルにしています。
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
球とレイの当たり判定
コンテンツパイプラインから読み込んだ ModelMesh クラスには BoundingSphere プロパティというメッシュを包括した球データが含まれています。このクラスの Intersects メソッドに先ほど作成した Ray を指定することによって球とレイが衝突しているかを調べることができます。
衝突する場合はレイの開始点と衝突ポイントの距離が返ります。衝突しない場合は null が返るので、サンプルでは null 判定で衝突しているか調べています。
ただし、このメソッドを使用する場合はモデルが原点に配置されていることが前提です。もし、モデルを移動させている場合は、レイをモデルの移動に合わせて座標変換する必要があります。
ちなみに今回のサンプルモデルは球なので、正確なあたり判定ができると思います。
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
this.isHit = true;
break;
}
}
サンプルプロジェクト
静的サイト