필요성
UI 요소를 Scene에 항상 존재하는 UI와 특정 상황에서만 팝업되는 UI로 구분하였다.
UIManager는 그중 팝업 UI를 관리하는 매니저이다.
팝업 UI는 사용자의 플레이에 따라 한번도 사용되지 않을 수 있다.
미리 Scene에 배치해놓고 disable 해놓을 수도 있지만 여러 개의 팝업 UI가 존재한다면 편집 작업이 불편해지고, 사용하지 않을 수도 있는 팝업 UI를 씬 활성화 시 모두 로드하게 되어 비효율적이다.
따라서 특정 팝업 UI를 사용할 때만 Resources 폴더에서 가져와 동적 생성하도록 만들어 리소스를 절약하려 한다.
설계 과정
1. T 타입의 팝업 UI를 만든다. 이때 class 이름(script 이름)과 prefab 이름은 동일해야하며 prefab에 해당 컴포넌트가 붙어있어야 한다. 또한 UIBase를 상속해야 한다. 기본으로 캔버스를 가지고 있게하기 위해서 상속시킨다. 이외에 팝업 UI가 공통으로 가져야 하는 속성이나 메서드가 있으면 UIBase에 추가 작성한다.
ex) GameoverPopup.cs 작성, GameoverPopup 게임 오브젝트를 prefab화 하고 GameoverPopup 컴포넌트 추가
2. 사용할 팝업 UI prefab을 Resources 폴더 내에 저장한다.
3. Load 메서드에서 팝업 UI를 불러와 생성한다. (팝업 UI를 넣을 캔버스를 설정)
3.1. 팝업 UI를 넣을 캔버스를 생성하고 크기, 상호작용, 랜더 모드를 설정한다.
3.2. 빈 팝업 UI 오브젝트를 생성하고 캔버스에 위치시킨다.
3.3. 최근에 생성된 팝업 UI가 최상단에 위치하도록 순서를 조정한다.
4. Show 메서드로 다른 클래스에서 호출하도록 만든다. (실제 사용할 팝업 UI를 생성하고 화면에 팝업)
ex) UIManager.Instance.Show<GameoverPopup>();
4.1. Resources 폴더에서 팝업 UI를 동적으로 불러와 생성한다.
4.2. Load 메서드를 사용해 생성한 팝업 UI를 빈 팝업 UI 오브젝트에 들어가도록 만든다.
4.3. 활성화된 UI 리스트에 새로 생성된 팝업 UI를 추가한다.
5. Hide 메서드로 사용하지 않는 팝업 UI를 삭제한다. 팝업 UI가 사라질 때 수행해야할 작업을 Hide 메서드에 추가 작성한다.
코드
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIManager : SingletonBase<UIManager>
{
// 고정 화면비 설정, Phone, for 해상도 대응
public float screenWidth = 720;
public float screenHeight = 1280;
[SerializeField] private List<UIBase> uiList = new List<UIBase>(); // UI 요소 관리하는 리스트
// UI 요소를 리소스 폴더에서 가져오고 화면에 표시하는 메서드
public T Show<T>() where T : UIBase
{
string uiName = typeof(T).ToString(); // UI 요소 이름을 T 타입으로 받기
UIBase go = Resources.Load<UIBase>("UI/" + uiName); // Resource 폴더에서 동적으로 프리팹 불러오기
/* 반드시 UI의 Script 이름과 Prefab 이름이 동일해야함 */
if (go == null) // 경로에 존재하지 않으면 로그로 알리고 null 반환
{
Debug.Log($"UI Load Failed. {uiName} doesn't exist in Resources/UI/");
return null;
}
var ui = Load<T>(go, uiName);
uiList.Add(ui);
return (T)ui;
}
// 캔버스와 UI 요소를 생성하고 UI를 캔버스에 위치하도록 설정하는 메서드
private T Load<T>(UIBase prefab, string uiName) where T : UIBase
{
GameObject newCanvasObject = new GameObject(uiName + "Canvas"); // 캔버스를 넣을 빈 게임오브젝트 생성
var canvas = newCanvasObject.AddComponent<Canvas>(); // 캔버스 컴포넌트 추가
canvas.renderMode = RenderMode.ScreenSpaceOverlay; // 캔버스 랜더 모드 설정
// 캔버스 크기 설정하는 컴포넌트 추가 후 크기 설정
var canvasScaler = newCanvasObject.AddComponent<CanvasScaler>();
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvasScaler.referenceResolution = new Vector2(screenWidth, screenHeight);
newCanvasObject.AddComponent<GraphicRaycaster>(); // 상호작용을 위한 컴포넌트 추가
UIBase ui = Instantiate(prefab, newCanvasObject.transform); // UI 요소를 캔버스의 자식으로 생성
ui.name = ui.name.Replace("(Clone)", ""); // 이름에서 (Clone) 삭제
ui.canvas = canvas; // 새로 생성된 UI의 캔버스를 기존에 만든 캔버스로 설정
ui.canvas.sortingOrder = uiList.Count; // 최근에 생성된 UI가 최상단에 보이도록 설정
return (T)ui;
}
public void Hide<T>() where T : UIBase
{
string uiName = typeof(T).ToString();
Hide(uiName);
}
public void Hide(string uiName)
{
UIBase go = uiList.Find(obj => obj.name == uiName); // UI 이름이 활성화된 UI 리스트에 있는지 탐색
uiList.Remove(go);
Destroy(go.canvas.gameObject);
}
}
using UnityEngine;
public class UIBase : MonoBehaviour
{
public Canvas canvas; // UIBase 상속하는 클래스가 기본적으로 캔버스 갖도록 하기
public void Hide()
{
UIManager.Instance.Hide(gameObject.name);
}
}'프로젝트 기록' 카테고리의 다른 글
| [트러블슈팅] 버튼 이벤트 인덱스 오류 (0) | 2025.10.01 |
|---|---|
| [HighRoWin] 상속과 인터페이스를 활용한 리팩토링 (0) | 2025.05.25 |
| [Warning-of-the-Monsters] 인간 상태 머신 설계 (0) | 2025.05.24 |
| PoolManager 설계 (0) | 2025.05.24 |