Unity Term Book
물리

Collision Events

Collision Events는 OnCollisionEnter·Stay·Exit 세 콜백으로 접촉점·충격·상대 속도를 전달합니다. Collision 객체로 사운드·파편·데미지를 만들고, Stay 남용을 피하며 FixedUpdate와 맞춰 안정적인 물리 반응을 설계하는 실전 가이드입니다.

상상해 보세요...

Collision Events는 사고 보험 리포트와 같습니다. 두 차가 충돌하면(OnCollisionEnter) 누가·얼마나 빠르게·어디서 부딪혔는지 기록합니다. 접촉이 유지되면(OnCollisionStay) 계속 남기고, 떨어지면(OnCollisionExit) 파일을 닫습니다. `Collision` 객체가 그 리포트 — 충격력, 월드 접촉점, 상대입니다.

개념 자세히

Unity Physics에서 두 Collider가 충돌하면(Trigger가 아닐 때) 엔진이 세 콜백을 호출합니다: 첫 프레임에 OnCollisionEnter(Collision), 이후 물리 프레임마다 OnCollisionStay(Collision), 분리 시 OnCollisionExit(Collision). Collision 인자에 전체 접촉 데이터가 담깁니다.

Collision이 노출하는 것: collision.gameObject(상대), collision.contacts(위치·normal이 있는 접촉점), collision.relativeVelocity(상대 속도 — 보통 충격 강도), collision.impulse(총 impulse).

이 콜백은 솔버 이후 물리 스텝에서 돕니다. 여기서 대량 스폰·에셋 로드를 하지 마세요 — 플래그를 세우거나 이벤트를 큐에 넣고 무거운 작업은 Update에서. 많은 몸이 서로 기대면 OnCollisionStay가 특히 비쌉니다 — Physics.reuseCollisionCallbacks = true를 검토하세요.

양쪽 모두 Collider가 필요하고, 적어도 하나는 non-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

relativeVelocity.magnitude 읽기

충격 강도로 사용: 약하면 가벼운 탭, 세면 데미지·파괴…

3

첫 접촉점 가져오기

collision.GetContact(0) 또는 contacts[0].point — 정확한 위치에 파티클·데칼 스폰.

4

Tag 또는 Layer로 필터

로직 전에 collision.gameObject.CompareTag("Enemy") — 무관한 히트는 무시.

5

OnCollisionStay 과다 사용 금지

“닿아 있는지”만 필요하면 Enter/Exit에서 bool을 세우고 Update에서 읽으세요.

인터랙티브 시뮬레이터

"Fire"를 눌러 벽에 발사체를 쏘고 — collision 이벤트가 순서대로 로그되는지 보세요.

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));
      }
  }
}

코드 예제

고급

충격이 충분하면 파괴 — 파편을 스폰하고 원본을 Destroy.

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 = 히트 면의
  • ▸Static-vs-static은 OnCollisionEnter 없음

⚠️ 흔한 실수

  • ❌ OnCollisionStay마다 매 프레임 물리 값 조작

    불안정한 떨림과 끊김

    ✅ Enter/Exit에서 플래그, FixedUpdate에서 로직

  • ❌ 길이 검사 없이 contacts[] 인덱싱

    contacts가 비어 있으면 IndexOutOfRange

    ✅ GetContact(0) 또는 contacts.Length > 0 확인