[C#/Unity] 이벤트

muchan
muchan
∙∙조회 19

개요

게임을 개발하다 보면 "적이 죽었을 때 점수를 올리고, 사운드를 재생하고, UI를 갱신해야 한다"는 상황이 자주 생긴다. 이때 모든 처리를 한 곳에 몰아넣으면 코드는 금방 엉망이 되고, 코드 간 의존성이 커지게 된다.

이런 문제를 해결해주는 것이 이벤트다

이벤트는 "어떤 일이 일어났을 때 관심 있는 쪽에서 알아서 반응하게 만드는 구조"다. 직접 함수를 호출하는 대신, 신호만 쏘면 등록된 쪽에서 각자 처리한다. 덕분에 코드 간의 의존성이 줄어들고, 기능을 추가하거나 수정할 때 훨씬 편해진다.


이벤트란?

  • 마우스 클릭, 키보드 입력, 데이터 변경, 게임 상태 전환 등 다양한 상황에서 활용됨
  • 객체 간에 “어떤 일이 발생했다”는 것을 알리는 메시징 시스템
  • 특정 상황이 발생했음을 다른 객체에게 알리는 메커니즘

주요 용어

  • 게시자(publisher): 이벤트를 발생시키는 객체
  • 구독자(subscriber): 이벤트를 받아 처리하는 객체
  • 이벤트 핸들러: 이벤트 발생 시 실행되는 메서드

이벤트는 delegate에 event 키워드를 추가한 것이다

주의사항

  • 이벤트는 외부에서 할당할 수 없다 -> 더하거나 빼기 연산만 가능( +=, -= )
  • 이벤트는 클래스 내부에서만 사용할 수 있다 -> 알맞은 타이밍에 빼기 하는 것을 생각해야함

이벤트 사용하기

null 조건 연산자

이벤트에 구독자가 없으면 null이므로 호출 시 예외가 발생할 수 있음

csharp
class Button
{
    public event EventHandler Click;

    public void OnClick()
    {
        // null 체크 후 호출 (기존 방식)
        if (Click != null)
        {
            Click();
        }

        // null 조건 연산자 사용 (권장)
        Click?.Invoke();
    }
}

null 체크하면서 이벤트를 호출하는 것 보다 ?. null 조건 연산자를 사용하여 코드를 간결하게 작성할 수 있다

이벤트 구독과 해제

  • 이벤트 구독: += 연산자
  • 이벤트 해제: -= 연산자

구독을 해제하지 않으면 메모리 누수가 발생할 수 있다


UnityEvent 연습해보기

유니티에서 이벤트를 호출하는 방법이 여러가지가 있지만 인스펙터에서 이벤트를 노출하는 방법인 UnityEvent를 연습

기본 UnityEvent (매개변수 없음)

csharp
public class Player : MonoBehaviour
{
    public int hp = 100;

    // 인스펙터에서 보이게 하려면 public 또는 [SerializeField]
    public UnityEvent OnDead;

    public void TakeDamage(int damage)
    {
        hp -= damage;

        if (hp <= 0)
        {
            OnDead?.Invoke();
        }
    }
}

인스펙터에 이벤트가 생성된걸 볼 수 있다.

+버튼을 눌러 객체(이벤트 호출 시 실행될 함수가 있는 객체)를 넣어서 함수를 설정해주면 이벤트 구독이 된다.

매개변수 있는 UnityEvent

csharp
public class OnHpChangedEvent : UnityEvent<int> { }

public class Player : MonoBehaviour
{
    public int hp = 100;

    public OnHpChangedEvent OnHpChanged;
    public UnityEvent OnDead;

    public void TakeDamage(int damage)
    {
        hp -= damage;
        OnHpChanged?.Invoke(hp);  // 현재 HP를 넘김

        if (hp <= 0)
        {
            OnDead?.Invoke();
        }
    }
}

구독하는 쪽

csharp
public class UIManager : MonoBehaviour
{
    [SerializeField] private Player player;

    private void OnEnable()
    {
        player.OnHpChanged.AddListener(UpdateHpBar);  
        player.OnDead.AddListener(ShowGameOver);
    }

    private void OnDisable()
    {
        player.OnHpChanged.RemoveListener(UpdateHpBar);  
        player.OnDead.RemoveListener(ShowGameOver);
    }

    private void UpdateHpBar(int currentHp)
    {
        Debug.Log($"HP 바 갱신: {currentHp}");
    }

    private void ShowGameOver()
    {
        Debug.Log("게임 오버 UI 표시");
    }
}

매개변수가 있는 이벤트는 같은 데이터형을 매개변수로 받는 함수를 구독하도록 해줘야한다.