본문 바로가기

Program Tip

유니티 Resources.Load<Sprite>() null을 반환하는 문제 수정(기록용)

반응형

유니티 에디터에서는 정상적으로 잘 되지만, Android Device에만 올라가면 Resources.Load<Sprite>(path) 함수가 문제를 일으키는 일이 종종 있다.

어떤 경우인지 기술해본다.

광고를 보고 리워드 아이템을 지급하기 위해서 팝업 등으로 획득한 리워드를 확인하기 위해 프리팹을 로드 하거나, UI 등의 게임 오브젝트에 접근하여 리소스 로드 등을 할때 주로 발생하는 문제로 확인된다.

이는 광고 등을 보고 콜백을 받을 경우 UI Thread가 아닌 Main Thread라는 것으로 콜백을 받게 된다. 이 Main Thread와 UI Thread는 서로 다른 Thread로 Main Thread에서 UI 등에 접근하기 위해서는 UI Thread의 타이밍에 접근해야 하는데, 그렇게 하지 않고 바로 UI 등 게임 오브젝트에 접근할 때 발생하는 문제다.

이를 해결하기 위한 방법은 Main Thread에서 받은 콜백을 잠시 대기를 하고 UI Thread 타이밍에 맞춰서 함수를 호출해주는 방식으로 해주면 된다.

대체 방법으로는 Coroutine을 이용하거나 Dispatcher.Invoke 를 만들어 UI Thread에서 필요한 작업을 할 수 있도록 수정해줘야 한다.

using System;

namespace ForestJ.Util
{
    public interface IDispatcher
    {
        void Invoke(Action fn);
    }

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

namespace ForestJ.Util
{
    public class Dispatcher : MonoBehaviour, IDispatcher
    {
        List<Action> pending = new List<Action>();
        private static Dispatcher _instance;
        public static Dispatcher Instance
        {
            get
            {
                if (_instance == null)
                {
                    var obj = FindObjectOfType<Dispatcher>();
                    if (obj != null)
                    {
                        _instance = obj;
                    }
                    else
                    {
                        var new_obj = new GameObject();
                        _instance = new_obj.AddComponent<Dispatcher>();
                        new_obj.name = _instance.GetType().Name;
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// Schedule code for execution in the main-thread
        /// </summary>
        /// <param name="fn"></param>
        public void Invoke(Action fn)
        {
            if (fn == null)
            {
                return;
            }
            lock (pending)
            {
                pending.Add(fn);
            }
        }

        /// <summary>
        /// Execute pending actions
        /// </summary>
        public void InvokePending()
        {
            lock (pending)
            {
                if (pending.Count > 0)
                {
                    for (int i = 0; i < pending.Count; i++)
                    {
                        var action = pending[i];
                        action();
                    }
                    pending.Clear();
                }
            }
        }

        void Update()
        {
            InvokePending();
        }
    }

}

위의 Dispatcher를 이용하여 콜백에서 필요한 함수를 등록해주면 UI Thread에서 해당 함수를 Invoke 해준다.

사용 예

Dispatcher.Instance.Invoke(BuyAdMovie);
반응형