본문 바로가기
Project/<teamECHO> 4-1

[PJ] EventDispatcher 사용하기

by 왹져박사 2024. 6. 2.
728x90

 전의 프로젝트에서 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);

 

 

 

728x90