에디터에서는 괜찮지만, 빌드 후 실행 시 "Graphics device is null." 오류가 나타날 수 있습니다. 이 오류의 원인은 다양하지만, TMP를 서브스레드에서 제어할 경우 나타날 수 있습니다. 이를 해결하기위해 메인스레드에서 제어할 수 있도록 하는 기능을 구현하였습니다.

 

💬 서론

  • 유니티 빌드 프로그램이 크래시가 난 후 player.log를 살펴보면 다음과같이 stacktrace와 함께 오류가 난 부분이 나타납니다. 이 함수가 호출되는 위치를 메인스레드에서 호출할 수 있도록 구현한 기능을 이용합니다.
Graphics device is null.
TMPro.TextMeshProUGUI:Awake()
UiTab:ToggleLerp(Boolean, Single)
PacketHandler:S_EnterGameHandler(PacketSession, IMessage)
PacketManager:MakePacket(PacketSession, ArraySegment`1, UInt16)
ServerSession:OnRecvPacket(ArraySegment`1)
ServerCore.PacketSession:OnRecv(ArraySegment`1)
ServerCore.Session:OnRecvCompleted(Object, SocketAsyncEventArgs)
System.Net.Sockets.<>c:<.cctor>b__367_10(IAsyncResult)
System.Threading.ThreadPoolWorkQueue:Dispatch()

 

⚒️ 구현

  • 싱글턴으로 Monobehavior을 상속받아 메인스레드에서 Upate를 호출하여 queue의 내용을 꺼내와 함수를 호출합니다.
using UnityEngine;
using System;
using System.Collections.Generic;

public class UnityMainThreadDispatcher : MonoBehaviour
{
    private static readonly Queue<Action> _executionQueue = new Queue<Action>();
    private static UnityMainThreadDispatcher _instance = null;

    public static UnityMainThreadDispatcher Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<UnityMainThreadDispatcher>();

                if (_instance == null)
                {
                    GameObject go = new GameObject("UnityMainThreadDispatcher");
                    _instance = go.AddComponent<UnityMainThreadDispatcher>();
                }
            }

            return _instance;
        }
    }

    private void Awake() 
    {
        UnityMainThreadDispatcher.Instance.Enqueue(() => Debug.Log("UnityMainThreadDispatcher Active!"));
    }

    private void Update()
    {
        lock (_executionQueue)
        {
            while (_executionQueue.Count > 0)
            {
                _executionQueue.Dequeue().Invoke();
            }
        }
    }

    public void Enqueue(Action action)
    {
        lock (_executionQueue)
        {
            _executionQueue.Enqueue(action);
        }
    }
}

 

✅ 적용

  • 매니저로서 역할을 수행할 수 있도록 Scene에 컴포넌트를 추가해줍니다.

 

  • 오류가 난 부분이 메인스레드에서 호출될 수 있도록 Action타입으로 함수를 호출해줍니다.
/// <summary>
/// 서버에 접속하면 네트워크 매니저가 호출
/// </summary>
public void ConnectedFromServer()
{
    UnityMainThreadDispatcher.Instance.Enqueue(() => 
    {
        // Auth 매니저 활성화
        AuthManager.Instance.Init();

        // 로딩 탭 비활성화
        UiTabCtrl.Clear(1.0f);
    });
}

 

🕹️ Unity Affiliate

  • Unity Affiliate Program 파트너로서 아래의 배너를 통해 접속하신 경우 수수료를 받을 수 있습니다.
  • 아래 배너의 에셋들은 '실시간 무료 에셋 랭킹'을 나타냅니다.
bonnate