2D 충돌 감지란?
게임에서 오브젝트들이 서로 부딪혔는지 감지하는 것은 매우 중요한 기능입니다. 예를 들어, 플레이어가 코인을 획득하거나, 총알이 적에게 맞았는지 확인하는 등의 상황에서 사용합니다.
기본 개념
- Collider2D: 2D 충돌 영역을 정의하는 컴포넌트
- Trigger: 물리 충돌 없이 감지만 하는 모드
- Collision: 실제 물리 충돌이 발생하는 모드
- Rigidbody2D: 물리 시뮬레이션을 위한 컴포넌트
1. Collider2D 설정
충돌을 감지하려면 먼저 Collider2D 컴포넌트를 설정해야 합니다.
Collider2D 종류
- BoxCollider2D: 사각형 충돌 영역
- CircleCollider2D: 원형 충돌 영역
- CapsuleCollider2D: 캡슐형 충돌 영역
- PolygonCollider2D: 다각형 충돌 영역
Trigger 설정
using UnityEngine;
public class Coin : MonoBehaviour
{
void Start()
{
// Collider2D 가져오기
Collider2D collider = GetComponent<Collider2D>();
if (collider != null)
{
// Trigger 모드로 설정 (물리 충돌 없이 감지만)
collider.isTrigger = true;
}
}
}
2. OnTriggerEnter2D() - 충돌 시작 감지
OnTriggerEnter2D()는 다른 오브젝트가 Trigger 영역에 처음 들어왔을 때 한 번만 호출됩니다.
기본 사용법
using UnityEngine;
public class FallingFireball : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
// 충돌한 오브젝트가 플레이어인지 확인
if (other.CompareTag("Player"))
{
Debug.Log("플레이어와 충돌!");
// 게임 오버 처리
if (GameManager.Instance != null)
{
GameManager.Instance.GameOver();
}
}
}
}
실제 예제: 코인 획득
using UnityEngine;
public class Coin : MonoBehaviour
{
public int coinValue = 10;
void OnTriggerEnter2D(Collider2D other)
{
// 플레이어가 코인에 닿았는지 확인
if (other.CompareTag("Player"))
{
// 점수 추가
GameManager.Instance.AddScore(coinValue);
// 코인 삭제
Destroy(gameObject);
}
}
}
실제 예제: 불덩어리 충돌
using UnityEngine;
public class FallingFireball : MonoBehaviour
{
private bool hasCollided = false;
void OnTriggerEnter2D(Collider2D other)
{
// 이미 충돌했으면 무시 (중복 충돌 방지)
if (hasCollided)
{
return;
}
// 플레이어와 충돌했는지 확인
if (IsPlayer(other))
{
hasCollided = true;
TriggerGameOver();
}
}
private bool IsPlayer(Collider2D other)
{
// "Player" 태그를 가지고 있거나 SheepController 컴포넌트가 있으면 플레이어
return other.CompareTag("Player") ||
other.GetComponent<SheepController>() != null;
}
private void TriggerGameOver()
{
if (GameManager.Instance != null)
{
GameManager.Instance.GameOver();
}
}
}
3. OnTriggerStay2D() - 충돌 유지 감지
OnTriggerStay2D()는 다른 오브젝트가 Trigger 영역에 있는 동안 매 프레임마다 호출됩니다.
기본 사용법
using UnityEngine;
public class DamageZone : MonoBehaviour
{
public float damagePerSecond = 10f;
void OnTriggerStay2D(Collider2D other)
{
// 플레이어가 데미지 존에 있는 동안
if (other.CompareTag("Player"))
{
// 매 초마다 데미지 주기
PlayerController player = other.GetComponent<PlayerController>();
if (player != null)
{
player.TakeDamage(damagePerSecond * Time.deltaTime);
}
}
}
}
OnTriggerEnter2D와 함께 사용
using UnityEngine;
public class FallingFireball : MonoBehaviour
{
private bool hasCollided = false;
void OnTriggerEnter2D(Collider2D other)
{
HandleCollision(other);
}
// OnTriggerEnter2D가 호출되지 않는 경우를 대비한 백업
void OnTriggerStay2D(Collider2D other)
{
HandleCollision(other);
}
private void HandleCollision(Collider2D other)
{
// 이미 충돌했으면 무시
if (hasCollided)
{
return;
}
// 플레이어와 충돌 확인
if (IsPlayer(other))
{
hasCollided = true;
TriggerGameOver();
}
}
private bool IsPlayer(Collider2D other)
{
return other.CompareTag("Player") ||
other.GetComponent<SheepController>() != null;
}
private void TriggerGameOver()
{
if (GameManager.Instance != null)
{
GameManager.Instance.GameOver();
}
}
}
4. OnTriggerExit2D() - 충돌 종료 감지
OnTriggerExit2D()는 다른 오브젝트가 Trigger 영역을 벗어났을 때 한 번만 호출됩니다.
기본 사용법
using UnityEngine;
public class SafeZone : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Debug.Log("안전 지대에 들어왔습니다!");
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Debug.Log("안전 지대를 벗어났습니다!");
}
}
}
5. OnCollisionEnter2D() - 물리 충돌 감지
OnCollisionEnter2D()는 물리 충돌이 발생했을 때 호출됩니다. Trigger가 아닌 실제 충돌입니다.
Trigger vs Collision
// Trigger 모드: 물리 충돌 없이 감지만
collider.isTrigger = true;
// OnTriggerEnter2D() 호출
// Collision 모드: 실제 물리 충돌 발생
collider.isTrigger = false;
// OnCollisionEnter2D() 호출
OnCollisionEnter2D 사용법
using UnityEngine;
public class Ball : MonoBehaviour
{
void OnCollisionEnter2D(Collision2D collision)
{
// 충돌한 오브젝트 정보
GameObject otherObject = collision.gameObject;
// 충돌 지점 정보
ContactPoint2D contact = collision.contacts[0];
Vector2 point = contact.point;
// 충돌 방향
Vector2 normal = contact.normal;
Debug.Log("충돌 발생: " + otherObject.name);
Debug.Log("충돌 지점: " + point);
}
}
6. Rigidbody2D 설정
충돌 감지를 위해서는 Rigidbody2D가 필요합니다.
Kinematic 설정
using UnityEngine;
public class FallingFireball : MonoBehaviour
{
void Start()
{
SetupRigidbody();
}
private void SetupRigidbody()
{
Rigidbody2D rb = GetComponent<Rigidbody2D>();
if (rb == null)
{
// Rigidbody2D가 없으면 추가
rb = gameObject.AddComponent<Rigidbody2D>();
}
// Kinematic으로 설정 (물리 영향 없이 이동)
rb.bodyType = RigidbodyType2D.Kinematic;
// 중력 비활성화
rb.gravityScale = 0;
// 회전 방지
rb.freezeRotation = true;
// 시뮬레이션 활성화 (충돌 감지를 위해 필요)
rb.simulated = true;
}
}
7. 실제 사용 예제
예제 1: 플레이어와 불덩어리 충돌
using UnityEngine;
public class FallingFireball : MonoBehaviour
{
private bool hasCollided = false;
void OnTriggerEnter2D(Collider2D other)
{
HandleCollision(other);
}
void OnTriggerStay2D(Collider2D other)
{
HandleCollision(other);
}
private void HandleCollision(Collider2D other)
{
// 중복 충돌 방지
if (hasCollided)
{
return;
}
// 플레이어 확인
if (other.CompareTag("Player") ||
other.GetComponent<SheepController>() != null)
{
hasCollided = true;
// 게임 오버
if (GameManager.Instance != null)
{
GameManager.Instance.GameOver();
}
}
}
}
예제 2: 아이템 획득
using UnityEngine;
public class Item : MonoBehaviour
{
public string itemType = "Coin";
public int value = 10;
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
PlayerController player = other.GetComponent<PlayerController>();
if (player != null)
{
// 아이템 획득 처리
player.CollectItem(itemType, value);
// 아이템 삭제
Destroy(gameObject);
}
}
}
}
예제 3: 데미지 존
using UnityEngine;
public class DamageZone : MonoBehaviour
{
public float damagePerSecond = 5f;
void OnTriggerStay2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
PlayerController player = other.GetComponent<PlayerController>();
if (player != null)
{
// 매 초마다 데미지
player.TakeDamage(damagePerSecond * Time.deltaTime);
}
}
}
}
8. 주의사항
1. Rigidbody2D 필수
// ✅ 충돌 감지가 되려면 최소한 한쪽에 Rigidbody2D가 있어야 함
// 둘 다 없으면 충돌 감지 안 됨
2. Trigger 설정
// Trigger 모드: OnTriggerEnter2D() 호출
collider.isTrigger = true;
// Collision 모드: OnCollisionEnter2D() 호출
collider.isTrigger = false;
3. 태그 사용
// ✅ 좋은 예: 태그로 확인
if (other.CompareTag("Player"))
{
// 처리
}
// ❌ 나쁜 예: 이름으로 확인 (느림)
if (other.name == "Player")
{
// 처리
}
4. 중복 충돌 방지
private bool hasCollided = false;
void OnTriggerEnter2D(Collider2D other)
{
if (hasCollided) return; // 중복 방지
hasCollided = true;
// 충돌 처리
}
9. 정리
충돌 감지 함수(function) 비교
| 함수(function) | 호출 시기 | 사용 용도 |
|---|---|---|
OnTriggerEnter2D() |
Trigger 영역에 처음 들어올 때 | 코인 획득, 아이템 획득 |
OnTriggerStay2D() |
Trigger 영역에 있는 동안 | 지속 데미지, 버프 영역 |
OnTriggerExit2D() |
Trigger 영역을 벗어날 때 | 영역 이탈 알림 |
OnCollisionEnter2D() |
물리 충돌 발생 시 | 물리 기반 충돌 |
필수 설정
- Collider2D: 충돌 영역 정의
- Rigidbody2D: 최소한 한쪽에 필요
- Trigger 설정: 용도에 따라 선택
- 태그 설정: 오브젝트 식별용
베스트 프랙티스
- 태그 사용: 이름 대신 태그로 확인
- null 체크: GetComponent() 후 null 체크
- 중복 방지: 플래그로 중복 충돌 방지
- 성능 고려: OnTriggerStay2D는 매 프레임 호출되므로 주의
연습 문제
-
플레이어가 코인에 닿으면 점수를 10점 추가하고 코인을 삭제하는 스크립트를 작성하세요.
-
플레이어가 독가스 영역에 있으면 매 초마다 체력을 5씩 감소시키는 스크립트를 작성하세요.
-
총알이 적에게 맞으면 적의 체력을 감소시키고 총알을 삭제하는 스크립트를 작성하세요.