当たり判定
Unityのコライダーのおかげで、当たり判定は自動的に計算され、「OnCollisionXXX」「OnTriggerXXX」で衝突があった時にお知らせを受け取る、処理ができるが、実際に裏側でなにを行っているのか理解してみましょう。
球体同士
まず、一番楽なのは、球体同士の当たり判定である。以下の図を確認し、考えてみましょう
![]()
|
円「C」(中心点 (cx, cy)、半径 cr )と「D」(中心点 (dx, dy)、半径 dr )があるとして、中心の間の距離は、中心の差のノルムである:
|
この距離は半径の和よりも小さければ、2つの円が衝突しているといえる。
|
|
|
つまり
![]() |
練習
Unityでコライダーなしの2つの球体を作成し、MySphereCollider を作成し、半径を設定できるようにしてください(注意:コライダーの半径とメッシュの半径を合わせてください)
つぎ、SphereCollisionCheck のスクリプトを作成し
- すべての MySphereCollider を取得
- コライダー同士の距離を計算
- 半径よりも小さければ、色を変える
MySphereCollider スクリプト
// 当たり判定:球体のコライダー
public class MySphereCollider : MonoBehaviour
{
// 半径
[SerializeField]
private float radius;
// 半径を返す
public float GetRadius()
{
return radius;
}
// 色を変える
public void ChangeColor(Color color)
{
// MeshRendererの色を変える
MeshRenderer mr = GetComponent<MeshRenderer>();
mr.material.color = color;
}
}
SphereCollisionCheck スクリプト
// コライダー同士の当たり判定を確認
public class SphereCollisionCheck : MonoBehaviour
{
// すべてのコライダー
private MySphereCollider[] colliders;
private void Start()
{
// FindObjectsByType = 「このスクリプトを付いているオブジェクトのすべて下さい」
colliders = FindObjectsByType<MySphereCollider>(FindObjectsSortMode.None);
}
void Update()
{
// 初期値はすべて「false」
bool[] isCollision = new bool[colliders.Length];
// 全部 vs 全部を比較
for (int i = 0; i < colliders.Length - 1; i++)
{
for (int j = i + 1; j < colliders.Length; j++)
{
MySphereCollider iObje = colliders[i];
MySphereCollider jObje = colliders[j];
// 中心と中心の距離を計算
float distance = (iObje.transform.position - jObje.transform.position).magnitude;
// 2つの半径の足し算を計算
float radiusSum = iObje.GetRadius() + jObje.GetRadius();
// 当たり判定
if (distance - radiusSum <= 0)
{
isCollision[i] = true;
isCollision[j] = true;
}
}
}
// 衝突しているコライダーだけの色を変える
for (int i = 0; i < isCollision.Length; i++)
{
if (isCollision[i])
colliders[i].ChangeColor(Color.red);
else
colliders[i].ChangeColor(Color.white);
}
}
}
箱同士(回転なし)
BoxCollider の処理を把握しましょう。2つの箱「C」と「D」があり、それぞれが「最小X」「最大X」「最小Y」「最大Y」の数値を持っている。

この場合は:
- ある箱の「最大X」は他の箱の「最小X」よりも小さければ → 衝突しない
- ある箱の「最大Y」は他の箱の「最小Y」よりも小さければ → 衝突しない
- それ以外、衝突する
3次元の場合はZも含める。
練習
Unityでコライダーなしの2つの立方体を作成し、MyBoxCollider を作成し、幅、高さ、深さを設定できるようにしてください(注意:コライダーの半径とメッシュの半径を合わせてください)
つぎ、BoxCollisionCheck のスクリプトを作成し
- すべての MyBoxCollider を取得
- 各軸の「最大」と「最小」を比較
- 衝突したら、色を変える
MyBoxCollider スクリプト
// 箱コライダー
public class MyBoxCollider : MonoBehaviour
{
// サイズ(幅、高さ、深さ)
[SerializeField]
private Vector3 size;
// 各軸の最大位置を返す
public Vector3 GetMax()
{
return transform.position + size / 2;
}
// 各軸の最小位置を返す
public Vector3 GetMin()
{
return transform.position - size / 2;
}
// 色を変える
public void ChangeColor(Color color)
{
// MeshRendererの色を変える
MeshRenderer mr = GetComponent<MeshRenderer>();
mr.material.color = color;
}
}
BoxCollisionCheck スクリプト
// MyBoxCollider同士の当たり判定を確認
public class BoxCollisionCheck : MonoBehaviour
{
// すべてのコライダー
private MyBoxCollider[] colliders;
private void Start()
{
// FindObjectsByType = 「このスクリプトを付いているオブジェクトのすべて下さい」
colliders = FindObjectsByType<MyBoxCollider>(FindObjectsSortMode.None);
}
void Update()
{
// 初期値はすべて「false」
bool[] isCollision = new bool[colliders.Length];
// 全部 vs 全部を比較
for (int i = 0; i < colliders.Length - 1; i++)
{
for (int j = i + 1; j < colliders.Length; j++)
{
// 最大と最小位置を取得
Vector3 iMax = colliders[i].GetMax();
Vector3 iMin = colliders[i].GetMin();
Vector3 jMax = colliders[j].GetMax();
Vector3 jMin = colliders[j].GetMin();
// 軸ずつを確認
if (iMax.x < jMin.x) continue;
if (iMax.y < jMin.y) continue;
if (iMax.z < jMin.z) continue;
// 逆の比較も
if (jMax.x < iMin.x) continue;
if (jMax.y < iMin.y) continue;
if (jMax.z < iMin.z) continue;
// 当たった!
isCollision[i] = true;
isCollision[j] = true;
}
}
// 衝突しているコライダーだけの色を変える
for (int i = 0; i < isCollision.Length; i++)
{
if (isCollision[i])
colliders[i].ChangeColor(Color.red);
else
colliders[i].ChangeColor(Color.white);
}
}
}
球体と箱
SphereCollider と BoxCollider の当たり判定もあるので、その処理を学ぼうその処理を学ぼう。

ここで、球体「D」の中心点(dx, dy)に、箱のある最も近い点「p=(px, py)」を求める:

そして、球体同士と同じように、球体の中心と点「p」の距離が半径よりも小さければ、衝突になる。

練習
BoxSphereCollisionCheck を作成し、
BoxSphereCollisionCheck の当たり判定
// 球体と箱の当たり判定
public class BoxSphereCollisionCheck : MonoBehaviour
{
// すべての箱
private MyBoxCollider[] boxes;
// すべての球体
private MySphereCollider[] spheres;
void Start()
{
boxes = FindObjectsByType<MyBoxCollider>(FindObjectsSortMode.None);
spheres = FindObjectsByType<MySphereCollider>(FindObjectsSortMode.None);
}
void Update()
{
// 初期値はすべて「false」
bool[] isBoxCollision = new bool[boxes.Length];
bool[] isSphereCollision = new bool[spheres.Length];
// すべての球体 vs すべての箱
for (int i = 0; i < spheres.Length; i++)
{
for (int j = 0; j < boxes.Length; j++)
{
// コライダーを準備
MySphereCollider s = spheres[i];
MyBoxCollider b = boxes[j];
// 球体の中心
Vector3 center = s.transform.position;
// 箱の最大と最小位置
Vector3 max = b.GetMax();
Vector3 min = b.GetMin();
// 球体の中心に一番近い点「p」を求める
Vector3 p = new Vector3();
p.x = Mathf.Clamp(center.x, min.x, max.x);
p.y = Mathf.Clamp(center.y, min.y, max.y);
p.z = Mathf.Clamp(center.z, min.z, max.z);
// 中心からpまでの距離が小さければ、当たっている
float dist = (p - center).magnitude;
if (dist < s.GetRadius())
{
isSphereCollision[i] = true;
isBoxCollision[j] = true;
}
}
}
// 衝突しているコライダーだけの色を変える
for (int i = 0; i < isBoxCollision.Length; i++)
{
if (isBoxCollision[i])
boxes[i].ChangeColor(Color.red);
else
boxes[i].ChangeColor(Color.white);
}
for (int i = 0; i < isSphereCollision.Length; i++)
{
if (isSphereCollision[i])
spheres[i].ChangeColor(Color.red);
else
spheres[i].ChangeColor(Color.white);
}
}
}





