2D 충돌 감지

Unity에서 2D 충돌을 감지하는 방법을 학습합니다. OnTriggerEnter2D, OnTriggerStay2D, OnCollisionEnter2D 등의 함수를 다룹니다.

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() 물리 충돌 발생 시 물리 기반 충돌

필수 설정

  1. Collider2D: 충돌 영역 정의
  2. Rigidbody2D: 최소한 한쪽에 필요
  3. Trigger 설정: 용도에 따라 선택
  4. 태그 설정: 오브젝트 식별용

베스트 프랙티스

  1. 태그 사용: 이름 대신 태그로 확인
  2. null 체크: GetComponent() 후 null 체크
  3. 중복 방지: 플래그로 중복 충돌 방지
  4. 성능 고려: OnTriggerStay2D는 매 프레임 호출되므로 주의

연습 문제

  1. 플레이어가 코인에 닿으면 점수를 10점 추가하고 코인을 삭제하는 스크립트를 작성하세요.

  2. 플레이어가 독가스 영역에 있으면 매 초마다 체력을 5씩 감소시키는 스크립트를 작성하세요.

  3. 총알이 적에게 맞으면 적의 체력을 감소시키고 총알을 삭제하는 스크립트를 작성하세요.