ゼロから実装する衝突判定シリーズ第三弾、球と直方体の衝突判定です。
考え方
直方体は複雑なのでまず二次元の長方形で考えます。
下図のように、矩形の中心を原点、最小点の座標を 、最大点の座標をとします。
円の半径は、中心座標はとします。
まずのxy座標がいずれもよりも大きい場合、最短距離はの長さとなります。
次にのx座標だけよりも大きく、y座標は矩形の範囲内にある場合を考えます。
この場合は下図のように、最短距離はのx座標-のx座標が表すベクトルの長さとなります。
同様に、のx座標が矩形の中にあり、y座標が最小点よりも小さい場合、
最短距離はのy座標-のy座標が表すベクトルの長さとなります。
このように、各軸について円の中心が矩形の内側にあるか外側にあるかを判定し、
外側にある場合のみ長さの計算に組み入れることで最短距離を求めることができます。
三次元の直方体に関しても軸が一つ増えるだけで考え方は同様です。
実装の前提
以前、球と球との当たり判定に関する記事を書きました。
下記のインタフェイスとクラスは上記の記事のものを流用します。
- ICollider: コライダー用のインタフェイス
- SampleCollideDetector: 毎フレームISphereとIColliderの衝突判定を行い、衝突していたら赤いSphereをGizmoに描画する
実装
直方体型のコライダーを作ります。
using UnityEngine; public class ColliderCube : MonoBehaviour, ICollider { [SerializeField] private Vector3 _center = Vector3.zero; public Vector3 Center { get { return _center; } } [SerializeField] private Vector3 _size = Vector3.one; public Vector2 Size { get { return _size; } } private Transform _transform; private void Awake() { _transform = transform; } /// <summary> /// 球との当たり判定 /// </summary> public bool CheckSphere(ISphere collider) { var cubeToLocal = Matrix4x4.TRS(_center, Quaternion.identity, Vector3.one); var worldToCube = cubeToLocal.inverse * transform.worldToLocalMatrix; // Cubeの空間における球の中心点 var sphereCenter = worldToCube.MultiplyPoint(collider.WorldCenter); // 距離の二乗を求める var sqLength = 0.0f; for (int i = 0; i < 3; i++) { var point = sphereCenter[i]; var boxMin = _size[i] * -0.5f; var boxMax = _size[i] * 0.5f; if (point < boxMin) { sqLength += (point - boxMin) * (point - boxMin); } if (point > boxMax) { sqLength += (point - boxMax) * (point - boxMax); } } // 距離の二乗が0だったらCubeの内部にSphereの中心があるということ if (sqLength == 0.0f) { return true; } return sqLength <= collider.Radius * collider.Radius; } private void OnDrawGizmos() { var cubeToLocal = Matrix4x4.TRS(_center, Quaternion.identity, Vector3.one); var worldToCube = cubeToLocal.inverse * transform.worldToLocalMatrix; // Gizmoの変数を変更 var preColor = Gizmos.color; var preMatrix = Gizmos.matrix; Gizmos.color = Color.blue; Gizmos.matrix = worldToCube.inverse; // Cubeを描画する Gizmos.DrawWireCube(Vector3.zero, _size); // Gizmoの変数を戻す Gizmos.matrix = preMatrix; Gizmos.color = preColor; } }
衝突判定は上記で説明した通りの方法で行っています。
結果
正常に判定されていることが確認できました。
参考
今回はCubeの空間に座標変換することでAABBと球との距離を求めましたが、
OBBと球との距離を求める下記のような方法もあるようです。