# 当たり判定

Unityのコライダーのおかげで、当たり判定は自動的に計算され、「`OnCollisionXXX`」「`OnTriggerXXX`」で衝突があった時にお知らせを受け取る、処理ができるが、実際に裏側でなにを行っているのか理解してみましょう。

## 球体同士

まず、一番楽なのは、球体同士の当たり判定である。以下の図を確認し、考えてみましょう

<table border="1" id="bkmrk-%E5%86%86%E3%80%8Cc%E3%80%8D%EF%BC%88%E4%B8%AD%E5%BF%83%E7%82%B9-%28cx%2C-cy%29%E3%80%81%E5%8D%8A%E5%BE%84" style="border-collapse: collapse; width: 100%; border-width: 0px;"><colgroup><col style="width: 52.3016%;"></col><col style="width: 47.7778%;"></col></colgroup><tbody><tr><td style="border-width: 0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/GHnimage.png)

</td><td style="border-width: 0px;">円「***C***」（中心点 ***(c<sub>x</sub>, c<sub>y</sub>)***、半径 ***c<sub>r</sub>*** ）と「***D***」（中心点 ***(d<sub>x</sub>, d<sub>y</sub>)***、半径 ***d<sub>r</sub>*** ）があるとして、中心の間の距離は、中心の差のノルムである：

<table border="1" style="border-collapse: collapse; width: 100.165%; border-width: 1px; background-color: rgb(255, 255, 255);"><colgroup><col style="width: 99.7396%;"></col></colgroup><tbody><tr><td>![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/JuTimage.png)

</td></tr></tbody></table>

</td></tr></tbody></table>

<span class="c5"> この距離は半径の和よりも小さければ、２つの円が衝突しているといえる。</span>

<table border="1" id="bkmrk-" style="border-collapse: collapse; width: 100%; border-width: 0px;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><tbody><tr><td style="border-width: 0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/0mKimage.png)

</td><td style="border-width: 0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/kqgimage.png)

</td></tr></tbody></table>

つまり

<table border="1" id="bkmrk--1" style="border-collapse: collapse; width: 100%; border-width: 1px; background-color: rgb(255, 255, 255);"><colgroup><col style="width: 99.881%;"></col></colgroup><tbody><tr><td>#### ![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/Knmimage.png)

</td></tr></tbody></table>

#### 練習

Unityで**コライダーなし**の２つの球体を作成し、MySphereCollider を作成し、半径を設定できるようにしてください（注意：コライダーの半径とメッシュの半径を合わせてください）

つぎ、SphereCollisionCheck のスクリプトを作成し

- すべての MySphereCollider を取得
- コライダー同士の距離を計算
- 半径よりも小さければ、色を変える

<details id="bkmrk-myspherecollider-%E3%82%B9%E3%82%AF%E3%83%AA"><summary>MySphereCollider スクリプト</summary>

```c#
// 当たり判定：球体のコライダー
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;
	}
}
```

</details><details id="bkmrk-spherecollisioncheck"><summary>SphereCollisionCheck スクリプト</summary>

```c#
// コライダー同士の当たり判定を確認
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;

				// ２つの半径の足し算を計算
				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);
		}
	}
}
```

</details>## 箱同士（回転なし）

BoxCollider の処理を把握しましょう。２つの箱「C」と「D」があり、それぞれが「最小Ｘ」「最大Ｘ」「最小Ｙ」「最大Ｙ」の数値を持っている。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/16gimage.png)

この場合は：

- ある箱の「最大Ｘ」は他の箱の「最小Ｘ」よりも小さければ → 衝突しない
- ある箱の「最大Ｙ」は他の箱の「最小Ｙ」よりも小さければ → 衝突しない
- それ以外、衝突する

３次元の場合はＺも含める。

#### 練習

Unityでコライダーなしの２つの立方体を作成し、MyBoxCollider を作成し、幅、高さ、深さを設定できるようにしてください（注意：コライダーの半径とメッシュの半径を合わせてください）

つぎ、BoxCollisionCheck のスクリプトを作成し

- すべての MyBoxCollider を取得
- 各軸の「最大」と「最小」を比較
- 衝突したら、色を変える

<details id="bkmrk-myboxcollider-%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88-"><summary>MyBoxCollider スクリプト</summary>

```c#
// 箱コライダー
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;
    }
}
```

</details><details id="bkmrk-boxcollisioncheck-%E3%82%B9%E3%82%AF"><summary>BoxCollisionCheck スクリプト</summary>

```c#
// 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);
        }
    }
}
```

</details>## 球体と箱

SphereCollider と BoxCollider の当たり判定もあるので、その処理を学ぼう。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/5dNimage.png)

ここで、球体「D」の中心点***（d<sub>x</sub>, d<sub>y</sub>）***に、箱のある最も近い点「***p=(p<sub>x</sub>, p<sub>y</sub>)***」を求める：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/MFPimage.png)

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

![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/iRYimage.png)

#### 練習

BoxSphereCollisionCheck を作成し、

- MyBoxCollider と MySphereCollider のすべてを求める
- １個ずつの球体に対して、すべての箱を確認
- 球体に一番近い点を求めて、半径よりも小さければ、衝突にする

<details id="bkmrk-boxspherecollisionch-1"><summary>BoxSphereCollisionCheck の当たり判定</summary>

```c#
// 球体と箱の当たり判定
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);
        }
    }
}
```

</details>