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
실습 단계
MonoBehaviour에 콜백 추가
OnCollisionEnter(Collision collision) — Unity가 호출하므로 이벤트 등록이 필요 없습니다.
relativeVelocity.magnitude 읽기
충격 강도로 사용: 약하면 가벼운 탭, 세면 데미지·파괴…
첫 접촉점 가져오기
collision.GetContact(0) 또는 contacts[0].point — 정확한 위치에 파티클·데칼 스폰.
Tag 또는 Layer로 필터
로직 전에 collision.gameObject.CompareTag("Enemy") — 무관한 히트는 무시.
OnCollisionStay 과다 사용 금지
“닿아 있는지”만 필요하면 Enter/Exit에서 bool을 세우고 Update에서 읽으세요.
인터랙티브 시뮬레이터
"Fire"를 눌러 벽에 발사체를 쏘고 — collision 이벤트가 순서대로 로그되는지 보세요.
"발사"를 눌러 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 확인