UI를 팝업 UI와 고정형 UI로 구분하여 생각하기
UI > Popup > UI_Popup.cs, UI > Scene > UI_Scene.cs
스크립트를 각각 만들어주고, 이 베이스 스크립트들은 MonoBehaviour 대신 UI_Base를 상속받는다
- 팝업과 씬 스크립트를 새로 만들 때 위 베이스 스크립트를 상속 받아서 사용
- Popup, Scene 폴더 내 스크립트들은 이제 UI_Base 대신 UI_Popup, UI_Scene 을 상속받으면 됨
UIManager _ui = new UIManager();
public static UIManager UI { get { return Instance._ui; } }
UIManager 스크립트를 만들어준 뒤, Manager.cs에 연결시켜준다
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");
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.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.overrideSorting = true;
if (sort)
{
canvas.sortingOrder = _order;
_order++;
}
else
{
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/Popup/{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))
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;
}
public void ClosePopupUI(UI_Popup popup)
{
if (_popupStack.Count == 0)
return;
if (_popupStack.Peek() != popup)
{
Debug.Log("Close Popup Failed!");
return;
}
}
public void ClosePopupUI()
{
if (_popupStack.Count == 0)
return;
UI_Popup popup = _popupStack.Pop();
Managers.Resource.Destory(popup.gameObject);
popup = null;
_order--;
}
public void CloseAllPopupUI()
{
while (_popupStack.Count > 0)
ClosePopupUI();
}
}
UIManager 스크립트에서 팝업을 여닫는 함수들을 만들어준다
* ClosePopupUI 함수는 버전을 두 가지로 만들어, 닫을 팝업의 이름을 명시해주는(안정성 높은) 버전도 사용할 수 있게 한다
* Stack 사용할 때는 count가 0인지 체크하는 습관을 들일 것
* canvas.overrideSorting: 부모의 sort order에 상관없이 자신만의 sort order를 가진다
UI_Button ui = Managers.UI.ShowPopupUI<UI_Button>();
Managers.UI.ClosePopupUI(ui);
위와 같이 사용하면 된다(안정성 높은 버전)
* 만일 스크립트명과 팝업프리팹명이 다르다면 ShowPopupUI 함수에 프리팹명을 인자로 넘겨주어야 한다
public class UI_Scene : UI_Base
{
public virtual void Init()
{
Managers.UI.SetCanvas(gameObject, false);
}
}
UI_Popup, UI_Scene 스크립트에 위와 같이 Init 함수를 만들어준다(popup은 두번째 인자(sort)가 true)
private void Start()
{
Init();
}
public override void Init()
{
base.Init();
// 기존 Start 함수에 들어가 있던 코드
}
그리고 UI_Button 스크립트(UI_Popup을 상속받는 스크립트)에 위와 같이 Init 함수를 override 해서 그 안에서 base(부모)의 Init 함수도 호출해준다
마지막으로 UI_Popup 스크립트에서 close popup 함수를 추가해준다
public class UI_Popup : UI_Base
{
public virtual void Init()
{
Managers.UI.SetCanvas(gameObject, true);
}
public virtual void ClosePopupUI()
{
Managers.UI.ClosePopupUI(this);
}
}
내부에서 close popup ui 함수를 좀 더 쉽게 호출하기 위함이다
+ UI 팝업 생성 시 뒤에 있는 부분이 클릭되는 것을 방지하기 위해 블로커를 만들어둔다
색상을 알파 0으로 맞춰두고 모든 공간을 차지하도록 사이즈를 키워주면 된다
아래에 위치할수록 늦게 랜더링 되기 때문에, Blocker는 맨 위에 올라가야 UI_Button 내 다른 것들을 누를 때 방해받지 않을 수 있다
'강의, 책 > [Unity] C#과 유니티로 만드는 MMORPG 게임 개발 시리즈' 카테고리의 다른 글
Section 7. UI - 코드 정리 (0) | 2024.01.31 |
---|---|
Section 7. UI - 인벤토리 실습 (0) | 2024.01.31 |
Section 7. UI - UI 자동화(이벤트) (0) | 2024.01.26 |
Section 7. UI - UI 자동화(바인딩) (0) | 2024.01.26 |
Section 6. Animation - State 패턴 (0) | 2024.01.24 |