UI를 관리하는 코드이다.
SceneUI는 Scene이 열렸을 때, 한번만 열리면 되지만, PopupUI는 Scene 중간에 올라올 수 있기 때문에, 스택으로 관리한다. 평소 게임에서 팝업이 열리고, 끄는 과정을 생각해보면 스택으로 관리하는 것이 편해보인다.
UIManager에는 UI 자동화를 위한 코드들이 구현 되어있다.
추가적으로, Util 클래스에 자주 사용하는 함수를 구현하여 놓았다.
UIManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIManager
{
int _order = 10;
Stack<UI_Popup> _popupStack = new Stack<UI_Popup>();
UI_Scene _sceneUI = null;
public GameObject Root
{
get
{
GameObject root = GameObject.Find("@UI_Root"); // UI의 최상위 부모를 찾는다
if (root == null)
root = new GameObject { name = "@UI_Root " }; // 없을 시 생성
return root;
}
}
public void SetCanvas(GameObject go, bool sort = true)
{
Canvas canvas = Util.GetOrAddComponent<Canvas>(go); // 인자로 받은 게임 오브젝트의 Canvas 컴포넌트를 받아온다
canvas.renderMode = RenderMode.ScreenSpaceOverlay; //UI를 카메라와 일정 거리에 두어 render한다
canvas.overrideSorting = true; // UI의 정렬 순서를 활성화 한다.
if (sort)
{
canvas.sortingOrder = _order; // 초기에 설정한 (10+@)의 값으로 정렬 순서를 설정한다
_order++; // 다음에 올라올 UI를 위해 정렬 값을 1 추가한다
}
else
{
canvas.sortingOrder = 0; // 인자로 받은 정렬 여부가 false일 경우 정렬 순서를 0으로 설정한다.
}
}
public T MakeSubItem<T>(Transform parent = null, string name = null) where T : UI_Base // UI_Base 또는 상속받는 타입만 사용
{ // UI 안의 자식 아이템을 생성하는 코드
if (string.IsNullOrEmpty(name)) // 만들 아이템의 이름을 설정하지 않았을 시
name = typeof(T).Name; // 타입, 클래스의 이름으로 설정한다.
GameObject go = Managers.Resource.Instantiate($"UI/SubItem/{name}"); // 해당하는 프리팹을 불러옴
if (parent != null) // 부모를 인자로 받았을 경우
go.transform.SetParent(parent); // 인자를 부모로 설정한다.
return Util.GetOrAddComponent<T>(go); // 불러온 프리팹에서 T 에 해당하는 컴포넌트를 가져오고, 없으면 더해서 가져온다.
}
public T ShowSceneUI<T>(string name = null) where T : UI_Scene // UI_Scene 또는 상속받는 타입만 사용가능
{ // SceneUI 를 보여주는 코드
if (string.IsNullOrEmpty(name)) // 이름을 설정하지 않았을 경우
name = typeof(T).Name; // 인자로 받은 제네릭 타입으로 이름을 설정한다.
GameObject go = Managers.Resource.Instantiate($"UI/Scene/{name}"); // 이름에 해당하는 프리팹을 생성한다.
T sceneUI = Util.GetOrAddComponent<T>(go); // 불러온 프리팹에 T 에서 해당하는 컴포넌트를 불러온다. 없으면 더해주고 불러온다.
_sceneUI = sceneUI;
go.transform.SetParent(Root.transform); // Root 클래스에 접근하여 @UI_Root를 생성하고, 그 아래에 붙여준다
return sceneUI; // 생성한 프리팹의 T에 해당하는 컴포넌트를 반환해 준다.
}
public T ShowPopupUI<T>(string name = null) where T : UI_Popup // UI_Popup 이거나 상속 받는 타입만 받는다.
{
if (string.IsNullOrEmpty(name)) // 인자로 이름을 설정하지 않았을 경우
name = typeof(T).Name; // 클래스 이릅으로 설정한다.
GameObject go = Managers.Resource.Instantiate($"UI/Popup/{name}"); // 프리팹을 불러온다
T popup = Util.GetOrAddComponent<T>(go); // T에 해당하는 컴포넌트를 프리팹에 붙여주고 가져온다.
_popupStack.Push(popup); // 팝업 처리 스택에 넣는다.
go.transform.SetParent(Root.transform); // @UI_Root 의 자식으로 설정한다.
return popup; // 컴포넌트를 반환한다.
}
public void ClosePopupUI(UI_Popup popup) // 팝업을 닫는다. (지정)
{
if (_popupStack.Count == 0) // 팝업 스택이 비어있으면
return; // 반환한다.
if (_popupStack.Peek() != popup) // 팝업 스택의 맨 위가 인자로 받은 팝업과 다르면
{
Debug.Log("Close Popup Failed"); // 팝업 닫기에 실패한다
return; // 팝업 스택 순서에 맞게 닫아야함.
}
ClosePopupUI();
}
public void ClosePopupUI() // 팝업을 닫는다. (자동, 맨위에 있는 거부터)
{
if (_popupStack.Count == 0)
return;
UI_Popup popup = _popupStack.Pop();
Managers.Resource.Destroy(popup.gameObject);
popup = null;
_order--;
}
public void CloseAllPopupUI() // 모든 팝업을 닫는다.
{
while (_popupStack.Count > 0)
ClosePopupUI();
}
public void Clear() // 초기화
{
CloseAllPopupUI();
_sceneUI = null;
}
}
Util.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Util
{
public static T GetOrAddComponent<T>(GameObject go) where T : UnityEngine.Component
{
T component = go.GetComponent<T>(); // 인자로 받은 게임오브젝트에서 T에 해당하는 컴포넌트를 가져온다
if (component == null) // 없을 경우
component = go.AddComponent<T>(); // 붙여준다.
return component;
}
public static GameObject FindChild(GameObject go, string name = null, bool recursive = false)
{
Transform transform = FindChild<Transform>(go, name, recursive); //인자로 받은 게임 오브젝트에서 name에 해당하는 자식을 찾는다
if (transform == null)
return null; // 찾지못하면 null리턴
return transform.gameObject; // 자식의 게임 오브젝트를 반환
}
public static T FindChild<T>(GameObject go, string name = null, bool recursive = false) where T : UnityEngine.Object
{
if (go == null) // 없는 게임 오브젝트를 인자로 받는다며 null 리턴
return null;
if (recursive == false) // 바로 아래 자식만 탐색한다.
{
for (int i = 0; i < go.transform.childCount; i++) // 인자로 받은 자식의 수 만큼 반복
{
Transform transform = go.transform.GetChild(i); // 모든 자식을 가져온다.
if (string.IsNullOrEmpty(name) || transform.name == name) // null이 아니거나, 자식의 이름이 인자로 받은 이름과 같은 경우
{
T component = transform.GetComponent<T>(); // 자식에서 컴포넌트를 받아온다.
if (component != null) // 컴포넌트가 존재할 경우
return component; // 반환
}
}
}
else // recursive가 true 인 경우
{ // 재귀적으로 찾는다는 의미
foreach (T component in go.GetComponentsInChildren<T>())
{ // GetComponentsInChildren 작동 방식이 DFS 랑 비슷하기 때문에 모든 자식 컴포넌트를 불러온다.
if (string.IsNullOrEmpty(name) || component.name == name)
return component;
}
}
return null;
}
}
'UNITY' 카테고리의 다른 글
UNITY - UI 자동화(2) - 인벤토리 구현 (0) | 2024.01.11 |
---|---|
UNITY - UI 자동화(1) - 인벤토리 구현 (0) | 2024.01.11 |
UNITY - Camera - 3인칭 시점(2) (4) | 2024.01.08 |
UNITY - Camera - 3인칭 시점(1) (3) | 2024.01.08 |
UNITY - InputManager (1) | 2024.01.08 |