내일배움캠프/TIL

[내배캠][Unity6기][TIL] UI Manager 디자인

binary는 호남선 2024. 11. 6. 19:31

- 재앙의 시작...저번 프로젝트에서 core한 부분을 맡았다가 실력에 한계를 느끼고 이번에 상대적으로 쉬워보이는 UI 도전!

- 이왕이면 좋은 설계를 하고 싶어서 튜터님께 drawcall, canvas 관련하여 질문하고 설계에 관한 조언을 얻으러갔다가 '동적 생성'이라는 큰 과제를 부여받는다...

UIManager

UI 동적 생성 과정

1. UI가 필요한 Scene의 Hierachy에 미리 UIManager가 있는 EventSystem 배치
2. Scene이 활성화되면 UIManager에서 현재 활성화된 Scene의 이름의 확인
3. Scene 이름과 일치하는 Assets/Resources/Prefabs/(Scene 이름) 폴더에 있는 모든 UI 가져오기
4. Scene에 'UI' 라는 이름의 빈 오브젝트 생성
5. UI 오브젝트 아래에 크기 맞춘 캔버스 동적 생성
6. 캔버스 아래에 가져온 UI 프리팹 동적 생성
7. Scene 전환 시 UI 파괴

 

- 특히 UIManager를 DonDestroyOnLoad로 만들지, 생성한 UI를 활성, 비활성화하는 방식으로 재사용할지에 대해 고민

- 결국에는 모두 재사용하지 않는 방향으로 결정, 재시작 시에 리셋된 데이터를 다시 받아오거나 참조 다시 바인딩하는 작업을 추가로 해야해서 깔끔하게 파괴 후 재생성하도록 설계

 

using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class UIManager : SingletonBase<UIManager>
{
    private GameObject _rootUI;
    private Canvas _canvas;

    protected override void Awake()
    {
        base.Awake();
        
        if (_rootUI != null)    // Scene에 UI 오브젝트가 있으면
        {
            Destroy(_rootUI);    // 기존 UI 삭제
            return;
        }
        CreateUIRoot();  // 없으면 UI 새로 생성
    }

    // 비활성화 시 UI 삭제
    public void OnDisable()
    {
        if (_rootUI != null)
        {
            Destroy(_rootUI);
            _rootUI = null;
        }
    }

    // 현재 활성화된 Scene의 UI 로드
    private void LoadSceneUI(Transform parent)
    {
        string folderName = SceneManager.GetActiveScene().name;
        string resourcePath = $"Prefabs/UI/{folderName}";
        CustomUtil.LoadAndInstantiatePrefabs(resourcePath, parent);
    }
    
    // 루트 UI GameObject와 Canvas 구조를 생성
    private void CreateUIRoot()
    {
        // UI의 루트 GameObject를 생성하고 UI로 이름 설정
        _rootUI = new GameObject("UI");

        // Canvas 생성하고 랜더링 모드 설정
        GameObject canvasObject = new GameObject("Canvas");
        _canvas = canvasObject.AddComponent<Canvas>();
        _canvas.renderMode = RenderMode.ScreenSpaceOverlay;

        // 해상도 대응
        CanvasScaler scaler = canvasObject.AddComponent<CanvasScaler>();
        scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        scaler.referenceResolution = new Vector2(1920, 1080);

        // UI 상호작용을 처리위한 컴포넌트 추가
        canvasObject.AddComponent<GraphicRaycaster>();

        canvasObject.transform.SetParent(_rootUI.transform);
        
        LoadSceneUI(canvasObject.transform);
    }
}

 

그렇게 완성된 UI Diagram! 최대한 종속성을 없애고 로직을 넣지 않으려고 했더니 많이 허전해졌다...