English

blog

円形と矩形(角度あり)の衝突判定

円と矩形(角度あり)

円形と角度のついた傾きありの矩形の衝突判定の実装の仕方を説明します。衝突判定(または当たり判定)とは、物体Aと物体Bが接触しているかを判定する事です。円はxy座標と半径を所持します。矩形はxy座標、横幅、縦幅、角度を持つとします。ここでは、矩形の回転は矩形の中心点を回転の中心とします。

アプレット、図、コードを用いて説明していきましょう。円形と角度変化なしの矩形の衝突判定の参考として、こちらの記事を読ませていただきました。

サンプルアプレット

こちらが衝突判定をデモするサンプルアプレットにです。もし二つの物体が接触していたら、色は青に変化します。緑で書かれた形は裏で計算に使っているものです。オレンジ色の線は矩形が0度の時の座標に戻した円の中心から矩形へ1番近い点を繋ぐ線です。入力欄に角度をRadianではなくDegreeで入力してください。ボタンを押すと角度が反映されます。マウスでクリック・ドラッグしますと円を動かせます。





円形と角度のある矩形の衝突判定の説明

考え方はシンプルです。計算する時、矩形を回転させないで、円を矩形の中心点回りに回転させます。つまり矩形を0度の角度のままにします。矩形の方を回転して計算しようとすると、右上・左下の座標を求める計算がより複雑になります。こうすれば、左上を(x, y)、右上を(x + width, y)、左下の座標を(x, y + height)で計算できます。

pic1

円を、矩形の角度分の回転をさせて、矩形0度の時の円形の座標に戻します。下の図を見てください。青色のものは実際の角度を持った形です。黒のものが計算する時の形です。矩形の角度は0度で、円の中心点を矩形中心点を軸に回転させてください。

使っている計算式は下記です。変数cx/cyは円の中心点、originX/originYは回転の軸(矩形の中心点)、x’/y’は角度を矩形が0度の時に戻した後の円の中心点です。

x’ = cos(theta) * (cx – originX) – sin(theta) * (cy – originY) + originX
y’ = sin(theta) * (cx – originX) + cos(theta) * (cy – originY) + originY

pic2

角度を戻した円の中心点から矩形へ1番近い点を求めます。if-else文でx/yそれぞれ簡単に把握できます。

まずはx座標からいきましょう。矩形の左のxをrx、矩形の右をrx + widthとし、円の中心点x(cx)と比較します。もしcxがrxより左の位置にあるなら、rxが1番近いx座標です。cxがrx + widthの右側にあるなら、rx + widthが1番近いx座標になります。それ以外ならcx自身が1番近いx座標です。下の図では、それぞれの円のx中心点1番近いx座標に赤い線をひきました。まだyを求めていないので1番近い座標はこの線上にあるとします。

pic3

同じ方法でy座標を求める事ができます。もし円のy中心点(cy)が矩形の上のy(ry)より上にあるならば、ryが1番近いです。cyが矩形の下のy座標(ry + height)より下にあるなら、ry + heightが1番近い位置です。この二つの条件に当てはまらないならば、cyに1番近い座標はcy自身です。

pic4

最後に先程見つけた1番近い座標から矩形0度の時の角度に戻した円の間の距離をピタゴラスの定理で求めます(a^2 + b^2 = c^2)。そして、その距離が円の半径より短ければ、二つの物体は衝突している事になります。

pic5

サンプルコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 矩形0度の時の座標に円の角度を直す。
double cx = Math.cos(rect.angle) * (circle.x - rect.centerX) - 
        Math.sin(rect.angle) * (circle.y - rect.centerY) + rect.centerX;
double cy = Math.sin(rect.angle) * (circle.x - rect.centerX) + 
        Math.cos(rect.angle) * (circle.y - rect.centerY) + rect.centerY;
 
// 上の円の中心点から矩形の1番近い座標
double x, y;
 
// 1番近いx座標を求める
if (cx < rect.x)
    x = rect.x;
else if (cx > rect.x + rect.width)
    x = rect.x + rect.width;
else
    x = cx;
 
// 1番近いy座標を求める
if (cy < rect.y)
    y = rect.y;
else if (cy > rect.y + rect.height)
    y = rect.y + rect.height;
else
    y = cy;
 
// 衝突判定
boolean collision = false;
 
double distance = findDistance(cx, cy, x, y);
if (distance < c.radius)
    collision = true; // 衝突
else
    collision = false;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * ピタゴラスの定理
 * @param x1
 * @param y1
 * @param x2
 * @param y2
 */
public double findDistance(double x1, double y1, double x2, double y2){
    double a = Math.abs(x1 - x2);
    double b = Math.abs(y1 - y2);
 
    double c = Math.sqrt((a * a) + (b * b));
    return c;
}

Leave a Comment