📢 발생한 버그

StartScene에서 다른 Scene으로 이동 후 다시 StartScene으로 돌와왔을 때 발생했다.

  • 오류 메시지
MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UIManager.OpenUI[T] (System.String path) (at Assets/Scripts/Managers/UIManager.cs:33)
InitStartScne.Start () (at Assets/Scripts/UI/StartSceneUI/InitStartScene.cs:8)

🤔 왜 발생했을까?

MissingReferenceException

해당 오류는 게임 오브젝트가 파괴되었지만 여전히 참조하려고 할 때 발생한다.

일반적으로 씬을 이동할 때 발생하는데, Unity는 기본적으로 씬이 변경될 때 모든 게임 오브젝트가 파괴된다.

그런데, UIManager가 싱글톤으로 설정되어 있고, 씬이 이동한 후에도 여전히 이전 인스턴스가 살아있다면,

이미 파괴된 게임 오브젝트에 대한 참조가 UIElements 딕셔너리에 남아있게 된다.

해결 방법으로 생각해본 것은

  1. 씬 이동 전에 UIManager 상태를 재설정하거나 UIElements 사전을 비우는 메소드를 호출한다.
  2. UIManager 싱글톤의 생명주기를 관리해서 새로운 씬으로 이동할 때 파괴되지 않게 한다.
  3. UIElements 사전에서 오브젝트를 찾을 때 마다 null 체크를 수행한다.

⚙️ 해결 시도

  • UIManager.cs 중 ResetUIManager()
public void ResetUIManager()
{
    foreach (var uiElement in UIElements.Values)
    {
        if (uiElement != null)
        {
            Destroy(uiElement);
        }
    }
    UIElements.Clear();
}

해당 코드를 UIManager에서 수정했다.

씬을 이동할 때 UI 요소를 유지해야 할 필요가 없다면, 씬 로드 직전에 RestUIManager를 실행한다.

이를 통해 새로운 씬에서 시작할 때 오래된 참조로 인한 오류를 방지하고, 메모리 누수를 방지 할 수 있다.

RestUIManager 메소드는 UIElements 딕셔너리 내 모든 요소를 파괴하고 사전을 비우므로, CLearUI를 각각의 UI 요소에 대해 호출할 필요가 없다.

그러나 특정 UI요소만을 파괴하고, 해당 씬에서 사용될 일이 없다면 ClearUI를 호출하면 된다.

  • UImanager.cs 중 ClearUI< T >()
 public void ClearUI<T> () where T : Component
 {
     string prefabName = typeof(T).Name;

     if(UIElements.TryGetValue(prefabName, out GameObject uiElement))
     {
         Destroy(uiElement);
         UIElements.Remove(prefabName);
     }
     else
     {
         Debug.LogWarning($"DestroyUI 호출됨: {prefabName} 타입의 UI 요소를 찾을 수 없다.");
     }
 }

이후 로딩씬을 실행할 때 UI 요소들을 리셋하는 메서드를 호출하자.

  • LoadingSceneController.cs 중 LoadScene()
public static void LoadScene(string sceneName)
{
    nextScene = sceneName;

    if (UIManager.Instance != null)
    {
        UIManager.Instance.ResetUIManager();
    }

    ResourceManager.Instance.UnLoadAllResource();

    SceneManager.LoadScene("LoadingScene");
}

✅ 해결!

더 이상 오류도 발생하지 않았고, 내가 원하는 로직대로 실행되었다!

0개의 댓글