【ゲーム数学】第五回 p5.jsで学ぶゲーム数学「球と線分の衝突判定」

スポンサーリンク
PC創作
マイケル
マイケル
みなさんこんにちは!
マイケルです!
エレキベア
エレキベア
クマ〜〜〜〜〜〜
マイケル
マイケル
今日も引き続き、ゲーム数学を進めていくよ!
今回は「球と線分の衝突判定」についてだ!
エレキベア
エレキベア
また衝突判定クマか〜〜
マイケル
マイケル
前回は円と線分の衝突判定だったけど、
少しレベルアップして三次元での衝突判定処理だ!
マイケル
マイケル
数学の知識が必要になってくるけど
がんばっていこう!!
エレキベア
エレキベア
次元が変わるくらいどうってことないクマ〜〜〜
スポンサーリンク

参考書籍と開発言語

マイケル
マイケル
勉強にするにあたっては前回同様、下記参考書を参考にしました!

ゲームを動かす数学・物理 R

グラフィックスプログラミング入門

マイケル
マイケル
どちらも高校数学の基礎から解説しているため、しばらく数学から離れている方でも読みやすいと思います!
エレキベア
エレキベア
一読してみるクマ
マイケル
マイケル
そしてサンプルプログラムの実装としては p5.js を使用しています!
気になった方はこちらも使用してみてくださいね!
Screenshot 2021 01 16 23 15 46

p5.js ダウンロードページ

エレキベア
エレキベア
グラフィックス特化のJavaScriptライブラリクマね
スポンサーリンク

球と線分の衝突判定

マイケル
マイケル
それじゃ早速やっていこう!

考え方

マイケル
マイケル
考え方としては、球の方程式と直線の方程式を使います!
Screenshot 2021 03 19 22 28 33
エレキベア
エレキベア
懐かしい式が出てきたクマね〜〜〜〜
マイケル
マイケル
問題はどうやって衝突の判定を行うか!
線分の始点と終点は、下記のようになります。
つまり球と線分の交点のtの値が0〜1の時に線分の範囲であるといえます!
Screenshot 2021 03 19 22 29 27
エレキベア
エレキベア
なるほどクマ!
でも球と線分の交点はどうやって求めるクマ?
マイケル
マイケル
球の方程式と直線の方程式を使って求めることができるんだ!
下記のように、球の方程式に直線の方程式を代入してみよう!
Screenshot 2021 03 19 22 29 01
マイケル
マイケル
そして、下記のようにまとめながら展開します!
Screenshot 2021 03 19 22 29 11
エレキベア
エレキベア
これはどこかで見たことがあるような形クマ
マイケル
マイケル
そう、二次方程式だ!
a、b、cをそれぞれまとめると、下記のように解の公式で解くことができる!
Screenshot 2021 03 19 22 29 21
エレキベア
エレキベア
解の公式なつかしクマ〜〜
マイケル
マイケル
解の公式は±で2種類あるから、最小値(最寄り)のtを求めて0〜1の間にあるかどうかを判定すれば衝突しているかどうかが分かるんだ!
Screenshot 2021 03 19 22 29 31
エレキベア
エレキベア
最初に衝突した最寄りのtの値を求めるクマね

実装

マイケル
マイケル
これまでの考え方を実装したものが以下になります!
/**
 * 球と線分の衝突判定
 * 球の方程式と直線の方程式から求める
 * @param {Point}  ball_p 球の位置
 * @param {number} ball_r 球の半径
 * @param {Point}  line_p 線分の開始位置
 * @param {Vec3}   line_v 線分のベクトル
 * @returns true:衝突している false:衝突していない
 */
 function checkBallColLine(ball_p, ball_r, line_p, line_v) {
  // 直線の方程式と球の方程式より、
  // ax^2 + bx + c の形に変形し、a, b, cそれぞれの要素を求める
  let r = ball_r;
  let xa = line_p.x - ball_p.x;
  let ya = line_p.y - ball_p.y;
  let za = line_p.z - ball_p.z;
  let a = line_v.x*line_v.x + line_v.y*line_v.y + line_v.z*line_v.z;
  let b = 2.0 * (line_v.x*xa + line_v.y*ya + line_v.z*za);
  let c = xa*xa + ya*ya + za*za - r*r;
  // 解の公式よりtの値を求める
  // 「b^2-4ac」の値が0以下の場合、衝突無し
  let d = b*b - 4.0*a*c;
  if (d < 0) {
    return false;
  }
  // tの値(2種類)を求める
  d = Math.sqrt(d);
  let t1 = (-b + d) / (2.0 * a);
  let t2 = (-b - d) / (2.0 * a);
  // 線分の最寄り(最小値)のtを設定
  let t = 2.0;
  if (t1 >= 0.0 && t1 <= 1.0 && t1 < t) {
    t = t1;
  }
  if (t2 >= 0.0 && t2 <= 1.0 && t2 < t) {
    t = t2;
  }
  // tが1より大きい場合、衝突無し
  if (t > 1.0) {
    return false;
  }
  return true;
}
エレキベア
エレキベア
ややこしそうクマが、さっきの話を思い出すとなんとなく分かるクマね
マイケル
マイケル
こちらのコードを使ってシミュレータを作ってみたので、
よければ触ってみてください!

See the Pen
0319_01_COLISION
by masarito617 (@masarito617)
on CodePen.

↑球と線分の衝突判定

マイケル
マイケル
下記のように、正常に衝突判定が行えていることが確認できます!
Screenshot 2021 03 19 20 12 37
Screenshot 2021 03 19 20 13 14
エレキベア
エレキベア
やったクマ〜〜〜〜〜
スポンサーリンク

球と直線の衝突判定

マイケル
マイケル
ここからはおまけですが、球と直線の衝突処理についても紹介します!

実装

マイケル
マイケル
処理内容はほぼ同じですが、直線のためtの範囲を判定する必要がありません!
「b^2-4ac」の値が0以上かどうかのみ判定することで、衝突有無が分かります!
/**
 * 球と直線の衝突判定
 * 球の方程式と直線の方程式から求める
 * @param {Point}  ball_p 球の位置
 * @param {number} ball_r 球の半径
 * @param {Point}  line_p 直線の任意位置
 * @param {Vec3}   line_v 直線のベクトル
 * @returns true:衝突している false:衝突していない
 */
function checkBallColStraightLine(ball_p, ball_r, line_p, line_v) {
  // 直線の方程式と球の方程式より、
  // ax^2 + bx + c の形に変形し、a, b, cそれぞれの要素を求める
  let r = ball_r;
  let xa = line_p.x - ball_p.x;
  let ya = line_p.y - ball_p.y;
  let za = line_p.z - ball_p.z;
  let a = line_v.x*line_v.x + line_v.y*line_v.y + line_v.z*line_v.z;
  let b = 2 * (line_v.x*xa + line_v.y*ya + line_v.z*za);
  let c = xa*xa + ya*ya + za*za -r*r;
  // 「b^2-4ac」の値が0以上の時、衝突しているとみなす
  let d = b*b - 4.0*a*c;
  return d >= 0;
}
エレキベア
エレキベア
平方根を求める部分が負の値の場合は解が見つけられないクマね
マイケル
マイケル
こちらのコードを使ったシミュレータは以下になります!

See the Pen
0319_02_COLISION
by masarito617 (@masarito617)
on CodePen.

↑球と直線の衝突判定

マイケル
マイケル
こちらも下記のように衝突判定を行えていることが分かります!
Screenshot 2021 03 19 20 11 43
Screenshot 2021 03 19 20 12 06
エレキベア
エレキベア
ちょろいクマ〜〜〜〜
スポンサーリンク

おわりに

マイケル
マイケル
というわけで今回は球と直線の衝突判定でした!
どうだったかな?
エレキベア
エレキベア
計算が少し複雑だったクマが、少しずつ解くと分かったクマ
マイケル
マイケル
衝突処理は引き出しとして覚えて損はないからどんどん覚えていこう!
マイケル
マイケル
それでは今日はこの辺で!
アデュー!!
エレキベア
エレキベア
クマ〜〜〜〜〜〜〜〜〜

【ゲーム数学】第五回 p5.jsで学ぶゲーム数学「球と線分の衝突判定」 〜完〜

コメント