Chapter 7-5. UI : UI Manager (팝업UI들 스택처럼 관리)

Date:     Updated:

카테고리:

태그:

인프런에 있는 Rookiss님의 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click

Chapter 7. UI

🚀 UI 자동화를 위한 클래스 구조

  • 📜Managers 👉 📜UIManager 를 싱글톤으로 관리
    UIManager _ui = new UIManager();
    public static UIManager UI { get { return Instance._ui; } }
    
  • 📜PlayerController 👉 📜UIManager의 UI 캔버스 프리팹 생성해주는 ShowPopupUI(팝업 UI 경우) 함수를 호출시켜 UI 띄우기
        void Start()
      {
          Managers.Input.MouseAction -= OnMouseClicked;
          Managers.Input.MouseAction += OnMouseClicked;
    
          // TEMP
          Managers.UI.ShowPopupUI<UI_Button>();
      }
    
  • 📜UIManager 는 게임 씬 상에서 생길 여러가지 *UI 캔버스 프리팹* 들의 생성과 삭제를 관리하는 스크립트로 사용할 것이다.
    • 팝업 UI 캔버스들은 렌더링 순서 또한 관리되어야 한다. sort order

캔버스 UI 종류

이 포스트 상에선 두가지로 구분

  • 고정UI (씬단위로 항상 화면 고정)
    • 대표 조상 📜UI_Scene
      • 📜UI_Inven
  • 팝업UI (사용자가 클릭해야 활성화, 가장 나중에 띄운 팝업부터 비활되도록 Stack 으로 관리할 것)
    • 대표 조상 📜UI_PopUp
      • 📜UI_Button

📜UI_Base

public abstract void Init();


📜UI_PopUp

public class UI_Popup : UI_Base
{
    public override void Init()
    {
        Managers.UI.SetCanvas(gameObject, true);
    }

    public virtual void ClosePopupUI()  // 팝업이니까 고정 캔버스(Scene)과 다르게 닫는게 필요
    {
        Managers.UI.ClosePopupUI(this);
    }
}
  • UI 캔버스 세팅 (오브젝트에 Canvas 컴포넌트 sorting order 세팅) 👉 Init
    • 📜UIManager 의 SetCanvas 호출
      • 팝업 UI니까 캔버스들을 sorting 해야 하므로 true 전달 (밑에 📜UIManager 참고)
  • UI 닫기 (캔버스 프리팹 파괴) 👉 ClosePopupUI
    • 팝업이니까 고정 캔버스(Scene)과 다르게 닫는게 필요
    • 📜UIManager 의 ClosePopupUI 호출


📜UI_Scene

public class UI_Scene : UI_Base
{
	public override void Init()
	{
		Managers.UI.SetCanvas(gameObject, false);
	}
}
  • UI캔버스 세팅 (오브젝트에 Canvas 컴포넌트 sorting order 세팅) 👉 Init
    • 📜UIManager 의 SetCanvas 호출
      • 고정 UI니까 sorting 할 필요가 없으므로 false 전달 (밑에 📜UIManager 참고) 고정 UI는 그냥 sort order 값이 가장 최소값인 0 으로 고정시킬 것. (가장 먼저 그려져 밑에서 그려지게)


🚀 UI Manager

  • SetCanvas 👉 go 오브젝트의 캔버스 컴포넌트 가져와(GetOrAddComponent를 통해 없다면 붙여서라도 가져옴) sort order값 세팅
  • Show~ 👉 캔버스 UI 프리팹 생성
  • Close~ 👉 캔버스 UI 오브젝트 파괴
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIManager
{
    int _order = 10; // 현재까지 최근에 사용한 오더

    Stack<UI_Popup> _popupStack = new Stack<UI_Popup>(); // 오브젝트 말고 컴포넌트를 담음. 팝업 캔버스 UI 들을 담는다.
    UI_Scene _sceneUI = null; // 현재의 고정 캔버스 UI
    public GameObject Root
    {
        get
        {
			      GameObject root = GameObject.Find("@UI_Root");
			      if (root == null)
				        root = new GameObject { name = "@UI_Root" };
                    return root;
		    }
    }
  • 고정 UI
    • sort order 값이 0 으로 고정. (가장 먼저 그려져 밑에서 그려지게)
    • 스택으로 관리될 필요 없음. 가장 바탕이 되는 UI이므로 하나만 있으니!
  • 팝업 UI
    • sort order을 편의상 고정 UI와 겹치지 않도록 10부터 시작하게 할 것이다. 팝업 캔버스 UI들은 10, 11, 12, 13,… 렌더링 순서를 부여받음. 늦게 생성된 것일 수록 늦은 순서! (가장 위에 그려지도록)
    • 여러개가 생길 수 있으며, 가장 나중에 생성된 팝업부터 먼저 닫혀야 하므로 생성된 팝업 캔버스 UI 오브젝트들을 스택으로 관리할 것.
  • @UI_Root라는 이름의 오브젝트를 없다면 만들어서라도 리턴해주는 프로퍼티 Root
    • 이게 필요한 이유는, Hierarchy 상의 오브젝트들도 마치 폴더 안에 있는것처럼 관련 있는 것들끼리 종류별로 이름을 구분한 빈 오브젝트의 자식으로 넣어 정리할 것이기 때문이다. UI 오브젝트들은 이 @UI_Root 빈 오브젝트 아래에 생성되게 그룹화할 것이라서 필요.
    public void SetCanvas(GameObject go, bool sort = true)
    {
        Canvas canvas = Util.GetOrAddComponent<Canvas>(go);
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        canvas.overrideSorting = true; // 캔버스 안에 캔버스 중첩 경우 (부모 캔버스가 어떤 값을 가지던 나는 내 오더값을 가지려 할때)

        if (sort)
        {
            canvas.sortingOrder = _order;
            _order++;
        }
        else // soring 요청 X 라는 소리는 팝업이 아닌 일반 고정 UI
        {
            canvas.sortingOrder = 0;
        }
    }

오브젝트의 캔버스 세팅

  • canvas에 Canvas 컴포넌트 가져옴(없으면 붙여서라도)
  • 캔버스의 RenderMode를 ScreenSpaceOverlay 모드로 세팅
  • overrideSorting을 통해 혹시라도 중첩캔버스라 자식 캔버스가 있더라도 부모 캔버스가 어떤 값을 가지던 나는 내 오더값을 가지려 할때 true
    • canvas.overrideSorting = true;
  • 팝업 UI의 경우 canvas.sortingOrder를 _order로 세팅하고 _order 증가시키기
  • 고정 UI의 경우 canvas.sortingOrder 값을 0 으로 세팅
	  public T ShowSceneUI<T>(string name = null) where T : UI_Scene
	  {
		   if (string.IsNullOrEmpty(name))
		    	name = typeof(T).Name;

	    	GameObject go = Managers.Resource.Instantiate($"UI/Scene/{name}");
		    T sceneUI = Util.GetOrAddComponent<T>(go);
        _sceneUI = sceneUI;

	    	go.transform.SetParent(Root.transform);

	    	return sceneUI;
	  }

	  public T ShowPopupUI<T>(string name = null) where T : UI_Popup
    {
        if (string.IsNullOrEmpty(name)) // 이름을 안받았다면 T로 ㄱㄱ
            name = typeof(T).Name;

        GameObject go = Managers.Resource.Instantiate($"UI/Popup/{name}");
        T popup = Util.GetOrAddComponent<T>(go);
        _popupStack.Push(popup);

        go.transform.SetParent(Root.transform);

		return popup;
    }

캔버스 프리팹 생성하기

  • 고정 UI 캔버스 프리팹 생성 ShowSceneUI
    • T는 📜UI_Scene 자식들만 가능
    • 프리팹을 Instantiate으로 생성하고 _sceneUI에 바인딩 후 이를 리턴
    • Root프로퍼티를 통해 @UI_Root의 자식으로 넣어줌
  • 팝업 UI 캔버스 프리팹 생성 ShowPopupUI
    • T는 📜UI_PopUp 자식들만 가능
    • 프리팹을 Instantiate으로 생성하고 popup에 바인딩 후 스택에 추가. 그리고 이를 리턴
    • Root프로퍼티를 통해 @UI_Root의 자식으로 넣어줌

_order++처리를 ShowPopupUI 에서 안하고 SetCanvas에서 해준 이유는, 캔버스 프리팹을 통해 생성되는 경우(ShowPopupUI 사용해서) 말고! 이미 게임 시작전부터 static 하게 존재하고 있던, 프리팹으로부터 생성된게 아닌 팝업 UI일 경우도 있을 수 있기 때문이다. 그런 경우엔 ShowPopupUI를 거치지 않기 때문에 sort order 관리가 안될 것이다. 그래서 SetCanvas에서 해준 것!

    public void ClosePopupUI(UI_Popup popup) // 안전 차원
    {
		    if (_popupStack.Count == 0) // 비어있는 스택이라면 삭제 불가
			      return;

        if (_popupStack.Peek() != popup)
        {
            Debug.Log("Close Popup Failed!"); // 스택의 가장 위에있는 Peek() 것만 삭제할 수 잇기 때문에 popup이 Peek()가 아니면 삭제 못함
            return;
        }

        ClosePopupUI();
    }

    public void ClosePopupUI()
    {
        if (_popupStack.Count == 0)
            return;

        UI_Popup popup = _popupStack.Pop();
        Managers.Resource.Destroy(popup.gameObject);
        popup = null;
        _order--; // order 줄이기
    }

    public void CloseAllPopupUI()
    {
        while (_popupStack.Count > 0)
            ClosePopupUI();
    }
}

팝업은 Close 시킬 함수가 필요하다.



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Unity Lesson 2 카테고리 내 다른 글 보러가기

댓글 남기기