using System; using System.Collections.Generic; using UnityEngine; namespace UnityEditorToolkit.Utils { /// /// Unity 메인 스레드에서 작업을 실행하기 위한 Dispatcher /// WebSocket 등 다른 스레드에서 Unity API를 호출할 때 사용 /// public class UnityMainThreadDispatcher : MonoBehaviour { private static UnityMainThreadDispatcher instance; private static readonly Queue executionQueue = new Queue(); private static readonly object @lock = new object(); /// /// Singleton 인스턴스 가져오기 /// public static UnityMainThreadDispatcher Instance() { if (instance == null) { // 메인 스레드에서만 GameObject 생성 가능 if (UnityEngine.Object.FindObjectOfType() == null) { var go = new GameObject("UnityMainThreadDispatcher"); instance = go.AddComponent(); DontDestroyOnLoad(go); } else { instance = UnityEngine.Object.FindObjectOfType(); } } return instance; } private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else if (instance != this) { Destroy(gameObject); } } /// /// 메인 스레드에서 실행할 작업 등록 /// /// 실행할 작업 public void Enqueue(Action action) { if (action == null) { throw new ArgumentNullException(nameof(action)); } lock (@lock) { executionQueue.Enqueue(action); } } /// /// 메인 스레드에서 실행할 작업 등록 (콜백 포함) /// /// 실행할 작업 /// 완료 후 콜백 public void Enqueue(Action action, Action callback) { if (action == null) { throw new ArgumentNullException(nameof(action)); } lock (@lock) { executionQueue.Enqueue(() => { try { action.Invoke(); callback?.Invoke(null); } catch (Exception ex) { callback?.Invoke(ex); } }); } } private void Update() { // 메인 스레드에서 큐에 있는 작업 실행 lock (@lock) { while (executionQueue.Count > 0) { var action = executionQueue.Dequeue(); try { action.Invoke(); } catch (Exception ex) { Debug.LogError($"[UnityMainThreadDispatcher] Error executing action: {ex.Message}\n{ex.StackTrace}"); } } } } private void OnDestroy() { if (instance == this) { instance = null; } } /// /// 큐에 있는 작업 개수 /// public int QueueCount { get { lock (@lock) { return executionQueue.Count; } } } /// /// 큐 비우기 /// public void ClearQueue() { lock (@lock) { executionQueue.Clear(); } } } }