Unity Project/<teamECHO> 4-1

[PJ] EventDispatcher 사용하기

왹져박사 2024. 6. 2. 17:22

 전의 프로젝트에서 EventManager를 사용하다, 점점 늘어나는 이벤트에 누락되거나 충돌하는 경우들이 생겨 EventDispatcher를 사용하였다. 

 이번 프로젝트는 네트워크 프로젝트라 더더욱 이벤트가 중요할 것이라 생각하여 다시 공부하였다. 

 

EventDispatcher Script

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

public delegate void EventHandler(short type);

public delegate void EventHandler<in T>(short type, T data);

public class EventDispatcher
{
    public static readonly EventDispatcher instance = new EventDispatcher();

    //생성자 
    private EventDispatcher() { }

    public class EventHandlerListData
    {
        public short EventType { get; }

        private readonly List<Delegate> _delegates;

        public EventHandlerListData(short type) : this(type, 8)
        {
        }

        public EventHandlerListData(short type, int size)
        {
            EventType = type;
            _delegates = new List<Delegate>(size);
        }

        public void Send()
        {
            var len = _delegates.Count;
            for (var i = 0; i < len; i++)
            {
                if (_delegates[i] == null)
                    continue;
                var tEvent = (EventHandler)_delegates[i];
                tEvent(EventType);
            }
        }

        public void Send<T>(T data)
        {
            var len = _delegates.Count;
            for (var i = 0; i < len; i++)
            {
                if (_delegates[i] == null)
                    continue;
                var tEvent = (EventHandler<T>)_delegates[i];
                tEvent(EventType, data);
            }
        }

        public void Clear()
        {
            _delegates.Clear();
        }

        public void ClearNullEventHandler()
        {
            var len = _delegates.Count;
            var newLen = 0;
            for (var j = 0; j < len; j++)
            {
                if (_delegates[j] == null) continue;

                if (newLen != j)
                    _delegates[newLen] = _delegates[j];
                newLen++;
            }

            _delegates.RemoveRange(newLen, len - newLen);
        }

        public bool TryAdd(Delegate handler)
        {
            var newEvent = true;

#if DEBUG
            if (_delegates.Contains(handler))
            {
                Debug.LogError("[Error] Repeat to add " + handler);
                newEvent = false;
            }
#endif

            if (newEvent)
            {
                _delegates.Add(handler);
            }

            return newEvent;
        }

        public bool TryRemove(Delegate handler)
        {
            var index = _delegates.IndexOf(handler);
            if (index != -1)
            {
                _delegates[index] = null; //원래 있던 부분
                return true;
            }

            return false;
        }
    }

    private readonly Dictionary<short, EventHandlerListData> _eventHandlerDic =
        new Dictionary<short, EventHandlerListData>();

    private readonly List<EventHandlerListData> _tempHandlerList = new List<EventHandlerListData>();

    public void AddEventHandler(short type, EventHandler handler)
    {
        AddEventHandlerInternal(_eventHandlerDic, type, handler);
    }

    public void RemoveEventHandler(short type, EventHandler handler)
    {
        RemoveEventHandlerInternal(_eventHandlerDic, type, handler);
    }

    public void AddEventHandler<T>(short type, EventHandler<T> handler)
    {
        try
        {
            AddEventHandlerInternal(_eventHandlerDic, type, handler);
        }
        catch
        {
            Debug.LogError("trying to add handler error, type:" + type + " handler:" + handler);
        }
    }

    public void RemoveEventHandler<T>(short type, EventHandler<T> handler)
    {
        RemoveEventHandlerInternal(_eventHandlerDic, type, handler);
    }

    public void SendEvent(short type)
    {
        var eventListeners = GetEventHandlerList(_eventHandlerDic, type, false);
        eventListeners?.Send();
    }

    public void SendEvent<T>(short type, T msg)
    {
        var eventListeners = GetEventHandlerList(_eventHandlerDic, type, false);
        eventListeners?.Send(msg);
    }

    public EventHandlerListData GetEventHandlerList(short type)
    {
        return _eventHandlerDic.TryGetValue(type, out var eventList) ? eventList : null;
    }

    public void Clear()
    {
        foreach (var eventData in _eventHandlerDic)
        {
            eventData.Value.Clear();
        }

        _eventHandlerDic.Clear();
    }

    public void PresizeHandler(short type, int size)
    {
        if (!_eventHandlerDic.ContainsKey(type))
        {
            _eventHandlerDic.Add(type, new EventHandlerListData(type, size));
        }
    }

    public void ClearNullEventHandler()
    {
        var count = _tempHandlerList.Count;
        if (count > 0)
        {
            for (var i = 0; i < count; i++)
            {
                var eventListeners = _tempHandlerList[i];
                eventListeners.ClearNullEventHandler();
            }

            _tempHandlerList.Clear();
        }
    }

    private void AddEventHandlerInternal(Dictionary<short, EventHandlerListData> dic, short type, Delegate handler)
    {
        var eventListeners = GetEventHandlerList(dic, type, true);
        // 새로운 핸들러 추가
        eventListeners?.TryAdd(handler);
    }

    private void RemoveEventHandlerInternal(Dictionary<short, EventHandlerListData> dic, short type,
        Delegate handler)
    {
        var eventListeners = GetEventHandlerList(dic, type, false);
        if (eventListeners == null)
            return;

        if (eventListeners.TryRemove(handler))
        {
            if (!_tempHandlerList.Contains(eventListeners))
                _tempHandlerList.Add(eventListeners);
        }
    }

    private EventHandlerListData GetEventHandlerList(Dictionary<short, EventHandlerListData> dic, short type,
        bool autoCreate)
    {
        if (dic.TryGetValue(type, out var eventList))
            return eventList;

        if (autoCreate)
        {
            eventList = new EventHandlerListData(type);
            dic.Add(type, eventList);
            return eventList;
        }

        return null;
    }
}

 

이벤트들을 정리한 enum 만들기

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


namespace NHR
{
    public class EventType 
    {
        //이벤트들
        public enum eEventType
        {
            NONE = -1, 
            Event_Test,
            Change_Scene
        }
    }

}

 

 

매개변수가 없는 이벤트 초기화

//형식
EventDispatcher.instance.AddEventHandler((int)(이벤트타입Class).(enum).(eventType), new EventHandler((type) =>
{
    (기능)
}));

//예시
//매개변수가 없는 이벤트 초기화
EventDispatcher.instance.AddEventHandler((int)NHR.EventType.eEventType.Change_Scene, new EventHandler((type) =>
{
    Debug.Log("<color=yellow>Event_Test</color>");
}));

 

매개변수가 있는 이벤트 초기화

//형식
EventDispatcher.instance.AddEventHandler<(매개변수 타입)((int)(이벤트타입Class).(enum).(eventType), new EventHandler<(매개변수 타입)>((type, (매개변수)) =>
{
    (기능)
}));

//예시
//eSceneType 매개변수가 있는 이벤트 초기화
EventDispatcher.instance.AddEventHandler<eSceneType>((int)NHR.EventType.eEventType.Change_Scene, new EventHandler<eSceneType>((type, scene) =>
{
    Debug.Log("change Scene");
    this.nowScene = scene;
    this.ChangeScene(this.nowScene);
}));

 

 

이벤트 호출

//형식_매개변수X
EventDispatcher.instance.SendEvent((int)(이벤트타입Class).(enum).(eventType));

//예시_매개변수X
EventDispatcher.instance.SendEvent((int)NHR.EventType.eEventType.Change_Scene);


//형식_매개변수O
EventDispatcher.instance.SendEvent<매개변수타입>((int)(이벤트타입Class).(enum).(eventType), (매개변수));
//예시_매개변수O
EventDispatcher.instance.SendEvent<eSceneType>((int)NHR.EventType.eEventType.Change_Scene, eSceneType.Main);