Unity Term Book
物理

Collision Events

Collision Events は OnCollisionEnter・Stay・Exit の三つのコールバックで、接触点・衝撃力・相対速度・相手 Collider を報告します。ヒット音や破片演出、破壊判定の実装パターンを実践的に学べます。図解とシミュレーターで接触データを追い、自学にも使えます。

想像してみてください...

Collision Events は事故の保険レポートのようなものです。二台がぶつかると (OnCollisionEnter)、誰が誰に、どれくらいの速さで、どこで当たったかが記録されます。 接触中(OnCollisionStay)は記録が続き、離れると(OnCollisionExit)ファイルが閉じます。 `Collision` オブジェクトがそのレポート——衝撃力、ワールド接触点、相手です。

概念の詳細

Unity Physics で二つの Collider が衝突すると(Trigger ではない場合)、エンジンは三つのコールバックを発火します: 最初のフレームで OnCollisionEnter(Collision)、 続く物理フレームごとに OnCollisionStay(Collision)、 離れたときに OnCollisionExit(Collision)Collision 引数が 接触データの全体を持ちます。

Collision が公開するもの:collision.gameObject(相手)、 collision.contacts(位置と法線付きの接触点)、 collision.relativeVelocity(相対速度——衝撃の強さとしてよく使う)、 collision.impulse(総インパルス)。

これらのコールバックはソルバー後の物理ステップで走ります。 ここで大量生成やアセット読み込みをしないでください——フラグを立てるかイベントをキューし、重い処理は Update へ。 OnCollisionStay は多くの剛体が互いに寄りかかると特に重い—— Physics.reuseCollisionCallbacks = true を検討してください。

両方に Collider が必要で、少なくとも一方に非 kinematic の Rigidbody が必要です。静的 Collider は Dynamic Rigidbody に当たればコールバックを受け取れますが、 静的同士は衝突を報告しません。

Collision オブジェクト内のデータ

Collision collision

├─
collision.gameObject// 衝突してきたオブジェクト
├─
collision.rigidbody// 相手オブジェクトの Rigidbody
├─
collision.collider// 該当する Collider
├─
collision.relativeVelocity// Vector3: 相対速度(magnitude = 衝撃の強さ)
├─
collision.impulse// Vector3: やり取りされた総インパルス
└─
collision.contacts[ ]// ContactPoint 配列
├─.point// Vector3: ワールド空間の接触位置
└─.normal// Vector3: 接触点での表面方向

ハンズオン手順

1

MonoBehaviour にコールバックを追加

OnCollisionEnter(Collision collision) — Unity が呼ぶのでイベント登録は不要です。

2

collision.relativeVelocity.magnitude を読む

衝撃の強さとして使う:軽い → 静かなタップ、重い → ダメージ、粉砕…

3

最初の接触点を取る

collision.GetContact(0) または collision.contacts[0].point — 正確なヒット位置にパーティクルやデカールを生成。

4

Tag や Layer でフィルタ

ロジックの前に collision.gameObject.CompareTag("Enemy") — 無関係なヒットを無視。

5

OnCollisionStay を使いすぎない

「触れているか」だけなら Enter/Exit で bool を立て、Stay ではなく Update で読む。

インタラクティブシミュレーター

"Fire" をクリックして弾を壁へ——衝突イベントが順にログされる様子を確認。

velocity: —

「発射」を押して collision events を確認...

コード例

基本

接触点で音を再生し、パーティクルを生成する。

using UnityEngine;

public class CollisionSounds : MonoBehaviour
{
  public AudioClip softHit, hardHit;
  public ParticleSystem impactFX;
  public float hardHitThreshold = 5f;

  void OnCollisionEnter(Collision collision)
  {
      float speed = collision.relativeVelocity.magnitude;

      // Pick a clip from impact speed
      AudioClip clip = speed > hardHitThreshold ? hardHit : softHit;
      AudioSource.PlayClipAtPoint(clip, collision.GetContact(0).point);

      // Spawn FX at the first contact
      if (impactFX != null)
      {
          ContactPoint contact = collision.GetContact(0);
          Instantiate(impactFX, contact.point,
              Quaternion.LookRotation(contact.normal));
      }
  }
}

コード例

上級

衝撃が十分強いと粉砕——破片を生成して元を破棄。

using UnityEngine;

public class BreakableObject : MonoBehaviour
{
  public GameObject[] fragmentPrefabs; // Debris pieces
  public float breakForceThreshold = 8f;
  public float fragmentExplosionForce = 200f;
  bool broken = false;

  void OnCollisionEnter(Collision collision)
  {
      if (broken) return;
      if (collision.relativeVelocity.magnitude < breakForceThreshold) return;

      broken = true;
      ContactPoint contact = collision.GetContact(0);

      // Spawn and scatter fragments
      foreach (GameObject frag in fragmentPrefabs)
      {
          GameObject piece = Instantiate(frag, transform.position,
              Random.rotation);

          Rigidbody rb = piece.GetComponent<Rigidbody>();
          if (rb != null)
              rb.AddExplosionForce(fragmentExplosionForce,
                  contact.point, 1f);
      }

      Destroy(gameObject);
  }
}

📌 要点

  • Enter → Stay → Exit、常にこの順
  • relativeVelocity.magnitude = 衝撃の強さ
  • contacts[0].point = ワールド空間のヒット位置
  • contacts[0].normal = ヒット時の面の向き
  • 静的同士は OnCollisionEnter を呼ばない

⚠️ よくあるミス

  • ❌ OnCollisionStay 毎フレームで物理をいじる

    不安定なジッターとヒッチ

    ✅ Enter/Exit でフラグを立て、ロジックは FixedUpdate

  • ❌ 長さチェックなしで contacts[] をインデックス

    contacts が空 → IndexOutOfRange

    ✅ GetContact(0) を使うか contacts.Length > 0 を確認