マジックアクション 計画中5 ピック判定
ピック判定とは、画面上をピック(クリック)したときどのオブジェクトが対象になったかを検出することです。
三次元表示されている画面のピック判定は結構厄介だったりします。検討中のマジックアクション(仮)ではソフトウェアレンダリングを行うため頂点バッファに画面上の座標とZ値をすべて格納することができますので、一部の判定が二次元となり処理の簡略化が狙えます。
実装したピック判定ロジックのテストを行いました。360×360ドットの画面左上から右下までピック判定を呼び出し、結果をグラフ化します。下図はその一部です。
ピック関数は画面上の座標を引数とし、ピックされたポリゴンメッシュの番号を返します。戻ってきた番号を適当な色番号に変換して画像としています。
362枚のメッシュで構成されたモデルを129600回ピック判定するのに約3秒を要しました(2006年春モデルのノートPC)。まずまずの速度であると思います。ゲーム中では簡略された衝突判定モデルを使うことで、さらに処理時間短縮が可能です。
望んだとおりの結果が得られているのですが、ここまで来るのに少し手間取りましたので記録を残しておこうと思います。
まず最初にテストした結果が下図です。
右下がプラスの座標系で時計回りのメッシュをピックするには以下の判定式を使います。ピックした点が三本の直線より右側にあるかどうかにより判定されます。
直線と無限の長さを持つ線のことです。三角形は本来有限の長さを持つ線分で構成されています。
(x1-x0)*(py-y0) - (y1-y0)*(px-x0) > 0
(x2-x1)*(py-y1) - (y2-y1)*(px-x1) > 0
(x0-x2)*(py-y2) - (y0-y2)*(px-x2) > 0
作成されたグラフの方を見ると、メッシュの境界あたりに所々隙間が開いています。これは、三角形の線上自体をピックしたためどの三角形の内部とも判定されなかったからです。
ゲーム中では三角形の境界は見せないようにするのが基本ですので、判定漏れという不具合を残すことになってしまいます。
では線上もピック判定に含めるとどうなるかをテストしたのが下図になります。
メッシュの全く存在しない場所の一部に誤認識があります。判定式は以下の通りです。
(x1-x0)*(py-y0) - (y1-y0)*(px-x0) >= 0
(x2-x1)*(py-y1) - (y2-y1)*(px-x1) >= 0
(x0-x2)*(py-y2) - (y0-y2)*(px-x2) >= 0
線上をピック範囲に含めると、三角形の面積がゼロで3本の線が重なっているとき、その直線上が全てピック範囲とされてしまいます。
画面右上にあるオブジェクトが左下でピック判定されることもありえますので、以前の判定式より悪化しているといえます。かといって直線ではなく線分を判定式に使用すると、演算量が3倍近くになってしまいます。
最終的に使用したのは以下の式です。
(x1-x0)*(y2-y0) - (y1-y0)*(x2-x0) > 0
(x1-x0)*(py-y0) - (y1-y0)*(px-x0) >= 0
(x2-x1)*(py-y1) - (y2-y1)*(px-x1) >= 0
(x0-x2)*(py-y2) - (y0-y2)*(px-x2) >= 0
一行目に追加された式により、点2が直線0-1上にある場合を対象外としています。
三次元表示されている画面のピック判定は結構厄介だったりします。検討中のマジックアクション(仮)ではソフトウェアレンダリングを行うため頂点バッファに画面上の座標とZ値をすべて格納することができますので、一部の判定が二次元となり処理の簡略化が狙えます。
実装したピック判定ロジックのテストを行いました。360×360ドットの画面左上から右下までピック判定を呼び出し、結果をグラフ化します。下図はその一部です。
ピック関数は画面上の座標を引数とし、ピックされたポリゴンメッシュの番号を返します。戻ってきた番号を適当な色番号に変換して画像としています。
362枚のメッシュで構成されたモデルを129600回ピック判定するのに約3秒を要しました(2006年春モデルのノートPC)。まずまずの速度であると思います。ゲーム中では簡略された衝突判定モデルを使うことで、さらに処理時間短縮が可能です。
望んだとおりの結果が得られているのですが、ここまで来るのに少し手間取りましたので記録を残しておこうと思います。
まず最初にテストした結果が下図です。
右下がプラスの座標系で時計回りのメッシュをピックするには以下の判定式を使います。ピックした点が三本の直線より右側にあるかどうかにより判定されます。
直線と無限の長さを持つ線のことです。三角形は本来有限の長さを持つ線分で構成されています。
(x1-x0)*(py-y0) - (y1-y0)*(px-x0) > 0
(x2-x1)*(py-y1) - (y2-y1)*(px-x1) > 0
(x0-x2)*(py-y2) - (y0-y2)*(px-x2) > 0
作成されたグラフの方を見ると、メッシュの境界あたりに所々隙間が開いています。これは、三角形の線上自体をピックしたためどの三角形の内部とも判定されなかったからです。
ゲーム中では三角形の境界は見せないようにするのが基本ですので、判定漏れという不具合を残すことになってしまいます。
では線上もピック判定に含めるとどうなるかをテストしたのが下図になります。
メッシュの全く存在しない場所の一部に誤認識があります。判定式は以下の通りです。
(x1-x0)*(py-y0) - (y1-y0)*(px-x0) >= 0
(x2-x1)*(py-y1) - (y2-y1)*(px-x1) >= 0
(x0-x2)*(py-y2) - (y0-y2)*(px-x2) >= 0
線上をピック範囲に含めると、三角形の面積がゼロで3本の線が重なっているとき、その直線上が全てピック範囲とされてしまいます。
画面右上にあるオブジェクトが左下でピック判定されることもありえますので、以前の判定式より悪化しているといえます。かといって直線ではなく線分を判定式に使用すると、演算量が3倍近くになってしまいます。
最終的に使用したのは以下の式です。
(x1-x0)*(y2-y0) - (y1-y0)*(x2-x0) > 0
(x1-x0)*(py-y0) - (y1-y0)*(px-x0) >= 0
(x2-x1)*(py-y1) - (y2-y1)*(px-x1) >= 0
(x0-x2)*(py-y2) - (y0-y2)*(px-x2) >= 0
一行目に追加された式により、点2が直線0-1上にある場合を対象外としています。