[Unity] 이벤트 매니저 시스템

네,가능합니다 ㅣ 2024. 11. 26. 09:00

개요

EventManager는 게임 내 이벤트들을 중앙에서 관리 및 연결 해주는 시스템입니다.

Pub-Sub 패턴 활용하여 구현해보겠습니다.

 

구성요소

GameEventType(열거형)

public enum GameEventType
{
    PlayerDead,
    GameOver,
    ScoreChanged,
    LevelComplete
    // 필요한 이벤트 타입들을 추가
}

 

EventManager의 핵심 기능

 

Subscribe: 이벤트 리스너 등록

Unsubscribe: 이벤트 리스너 제거

Dispatch: 이벤트 발행

 

using System;
using System.Collections.Generic;
using UnityEngine;

namespace Common.Event
{
    public delegate void EventListener(object args);

    public static class EventManager
    {

        //eventListener저장해주는 Dictionary
        private static readonly Dictionary<GameEventType, List<EventListener>> eventListenerDic = new Dictionary<GameEventType, List<EventListener>>();

        /// <summary>
        /// 구독하는 함수
        /// </summary>
        public static void Subscribe(GameEventType type, EventListener listener)
        {
            if (!eventListenerDic.TryGetValue(type, out var list))
            {
                list = new List<EventListener>();
                eventListenerDic[type] = list;
            }

            list.Add(listener);
        }

        /// <summary>
        /// 구독취소하는 함수
        /// </summary>
        public static void Unsubscribe(GameEventType type, EventListener listener)
        {
            if (!eventListenerDic.TryGetValue(type, out var list))
            {
                return;
            }

            list.Remove(listener);
            if (list.Count == 0)
            {
                eventListenerDic.Remove(type);
            }
        }

        /// <summary>
        /// 이벤트 달성 시 구독한 리스트 실행해주는 함수
        /// </summary>
        public static void Dispatch(GameEventType type, object arg)
        {
            if (!eventListenerDic.TryGetValue(type, out var list))
            {
                return;
            }

            foreach (var listener in list)
            {
                try
                {
                    listener.Invoke(arg);
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            }
        }
    }
}

 

사용 방법

(기본적인 사용 예제)

public class PlayerController : MonoBehaviour
{
    private void OnEnable()
    {
        // 이벤트 구독
        EventManager.Subscribe(GameEventType.PlayerDead, OnPlayerDead);
    }

    private void OnDisable()
    {
        // 구독 해제
        EventManager.Unsubscribe(GameEventType.PlayerDead, OnPlayerDead);
    }

    private void OnPlayerDead(object args)
    {
        if (args is int damage)
        {
            Debug.Log($"플레이어가 사망했습니다.");
        }
    }

    private void TakeDamage(int damage)
    {
        if (health <= 0)
        {
            // 이벤트 발생
            EventManager.Dispatch(GameEventType.PlayerDead, damage);
        }
    }
}

 

(다양한 데이터 전달 예제)

 

// 단일 값 전달
EventManager.Dispatch(GameEventType.ScoreChanged, 100);

// 복합 데이터 전달
var gameData = new GameData { score = 100, level = 2 };
EventManager.Dispatch(GameEventType.GameStateChanged, gameData);

// 데이터 없이 사용
EventManager.Dispatch(GameEventType.GameOver, null);

 

장점

1. 중앙에서 관리(모든 이벤트를 한곳에서 관리하여 유지보수가 용이)

2. 느슨한 결합(클래스간의 직접 참조가 아님, 모듈성과 재사용성 향상)

3. 열거형타입을 이용함(휴먼에러 방지)

4. 유연한 데이터 전달(object타입을 통해 다양한 데이터 전달 가능, null값으로도 사용 가능)

 

실제 사용 시나리오

public class ScoreManager : MonoBehaviour
{
    private int currentScore = 0;

    private void OnEnable()
    {
        EventManager.Subscribe(GameEventType.ScoreChanged, OnScoreChanged);
    }

    private void OnDisable()
    {
        EventManager.Unsubscribe(GameEventType.ScoreChanged, OnScoreChanged);
    }

    private void OnScoreChanged(object args)
    {
        if (args is int points)
        {
            currentScore += points;
            UpdateUI();
        }
    }
}

public class Enemy : MonoBehaviour
{
    public void OnDeath()
    {
        // 적 처치 시 점수 부여
        EventManager.Dispatch(GameEventType.ScoreChanged, 100);
    }
}

 

public class UIController : MonoBehaviour
{
    public Text scoreText;
    public GameObject gameOverPanel;

    private void OnEnable()
    {
        EventManager.Subscribe(GameEventType.ScoreChanged, UpdateScoreUI);
        EventManager.Subscribe(GameEventType.GameOver, ShowGameOver);
    }

    private void OnDisable()
    {
        EventManager.Unsubscribe(GameEventType.ScoreChanged, UpdateScoreUI);
        EventManager.Unsubscribe(GameEventType.GameOver, ShowGameOver);
    }

    private void UpdateScoreUI(object args)
    {
        if (args is int score)
        {
            scoreText.text = $"Score: {score}";
        }
    }

    private void ShowGameOver(object args)
    {
        gameOverPanel.SetActive(true);
    }
}