| 졸업작품 프로젝트의 UI 구조 설명
교내 졸업작품에서 화면에 띄우는 UI들을 편리하게 관리하기 위해 만든 UI 구조입니다.
간략하게 설명하자면,
모든 화면에 띄우는 UI들이 상속받는 공통 부모 클래스 PopupUI가 있습니다.
PopupUI는 CanvasGroup을 가지며 Canvasgroup의 알파값을 조정하여 키거나 끄는 ShowPanel 메소드와 HidePanel 메소드가 있습니다. 그리고 UI의 RectTransform을 일정값 움직이는 MovePanel 메소드가 있습니다.
이 PopupUI들을 씬의 Canvas에서 가져와 UIManager의 딕셔너리와 스택에서 관리합니다.
UIManager의 메소드에 내가 띄우려는 UI의 이름(string값)을 입력하면 어디에서나 UI를 편하게 띄우고 없앨 수 있습니다.
| 코드 상세 설명
PopupUI 코드
public class PopupUI : MonoBehaviour
{
[Header("Popup Setting")]
[Space(10f)]
[SerializeField]
private float _panelFadeTime; //파넬이 띄워질 때 걸리는 시간 (fade시간)
[SerializeField]
private float _panelDelayTime; //파넬을 띄우기 전 대기 시간
protected CanvasGroup _panel;
protected RectTransform _rectTransform;
private Coroutine _showCoroutine = null;
private Coroutine _showAndHideCoroutine = null;
public virtual void Awake()
{
_panel = GetComponent<CanvasGroup>();
_rectTransform = GetComponent<RectTransform>();
if (_panel != null)
{
_panel.alpha = 0; //시작하면 UI의 알파값을 0으로 맞춰 안보이게
_panel.blocksRaycasts = false; //클릭 안되게
}
}
public virtual void ShowPanel() //UI를 켜는 함수
{
if (_showCoroutine != null)
StopCoroutine(_showCoroutine);
_rectTransform.SetAsLastSibling(); //UI를 최상단으로
_showCoroutine = StartCoroutine(ShowPanelCoroutine(_panelDelayTime));
}
private IEnumerator ShowPanelCoroutine(float delayTime)
{
yield return new WaitForSeconds(delayTime); //딜레이 기다렸다가
UIManager.Instance.currentPopupUI.Push(this); //지금 UI를 UIManager의 스택에 넣어줌
_panel.blocksRaycasts = true;
_panel.DOFade(1, _panelFadeTime).SetUpdate(true);
}
public virtual void HidePanel()
{
UIManager.Instance.currentPopupUI.TryPop(out var popupUI);
_panel.blocksRaycasts = false;
_rectTransform.SetAsFirstSibling(); //UI를 최하단으로
_panel.DOFade(0, _panelFadeTime).SetUpdate(true);
}
public virtual void ShowAndHidePanel(float waitTime)
{
if (_showAndHideCoroutine != null)
StopCoroutine(_showAndHideCoroutine);
_showAndHideCoroutine = StartCoroutine(ShowHideCoroutine(waitTime));
}
private IEnumerator ShowHideCoroutine(float waitTime)
{
_panel.DOFade(1, _panelFadeTime);
yield return new WaitForSeconds(waitTime);
_panel.DOFade(0, _panelFadeTime);
}
//x값 y값을 받아 그 위치로 RectTransform을 움직이는 함수. ease가 true로 들어오면 Easing을 사용
public virtual void MovePanel(float x, float y, float fadeTime, bool ease = true)
{
var tween = _rectTransform.DOAnchorPos(new Vector2(x, y), fadeTime);
if (ease) tween.SetEase(Ease.OutBack, 0.9f);
}
}
먼저 가장 기본이 되는 CanvasGroup과, UI의 RectTransform을 갖습니다. 시작하면 CanvasGroup의 알파값을 0으로 설정해 보이지 않게 만들고, blockRaycasts를 false로 설정해 UI 클릭이 되지 않게 만듭니다.
UI가 띄워지거나 사라질 때 몇 초만에 나타나게 할 것인지 _panelFadeTime 변수로 설정합니다. 또 UI를 띄울 때 바로 나타나지 않고 몇초 뒤에 띄울 것인지 _panelDelayTime 변수로 설정합니다.
ShowPanel 메서드
- 현재 UI를 띄우는 메서드입니다.
먼저 RectTransform.SetAsLastSibling을 사용해 현재 UI의 우선순위를 맨 위로 올립니다.
그 다음 코루틴을 사용하여 _panelDelayTime만큼 기다린 후에, UIManager의 Stack에 현재 UI를 넣어줍니다. (Stack에 넣는 이유는, 가장 최근의 PopupUI를 알기 위함입니다. UIManager의 로직에서 사용됩니다.)
그리고 DoTween의 DOFade 함수를 이용하여 _panelFadeTime 동안 UI를 띄웁니다.
HidePanel 메서드
- 현재 UI를 사라지게 하는 메서드입니다.
먼저 RectTransform.SetAsFirstSibling을 사용해 현재 UI의 우선순위를 맨 아래로 내립니다.
DoTween의 DOFade 함수를 이용하여 _panelFadeTime 동안 UI를 사라지게 합니다.
MovePanel 메서드
- 현재 UI를 특정 방향으로 움직이게 하는 메서드입니다.
DoTween의 DOAnchorPos 함수를 이용하여 현재 UI의 RectTransform값을 매개변수로 들어온 값으로 움직입니다.
매개변수로 ease bool변수가 true 로 들어왔다면, Easing을 해서 움직입니다.
UI들은 PopupUI를 상속받고, 거기서 필요한 메서드들만 상속받아 사용합니다.
UIManager 코드
using System.Collections.Generic;
using UnityEngine;
public class UIManager : Singleton<UIManager>
{
[HideInInspector]
public Transform canvasTrm; //캔버스의 위치
public Dictionary<string, PopupUI> popupUIDictionary = new(); //PopupUI들을 담아 관리하는 딕셔너리. Key값은 UI의 이름으로 받음
public Stack<PopupUI> currentPopupUI = new Stack<PopupUI>(); //가장 최근의 PopupUI를 알기 위한 스택.
public override void Awake()
{
canvasTrm = GameObject.Find("Canvas").transform;
PopupUI[] popupUIs = canvasTrm.GetComponentsInChildren<PopupUI>(); //캔버스에서 PopupUI들을 다 가져옴
foreach (PopupUI popupUI in popupUIs)
{
if (!popupUIDictionary.ContainsKey(popupUI.name))
{
popupUIDictionary.Add(popupUI.name, popupUI); //중복키가 없다면 해당 PopupUI를 딕셔너리에 넣어줌
}
else
{
Debug.LogWarning($"중복 키 : {popupUI.name}");
}
}
}
public void ShowPanel(string uiName, bool isOverlap = false) //UI의 이름을 받고
{
popupUIDictionary.TryGetValue(uiName, out PopupUI popupUI); //딕셔너리에서 검색하여
if (popupUI != null)
{
popupUI.ShowPanel(); //해당 PopupUI를 띄워줌
}
}
public void HidePanel(string uiName) //UI의 이름을 받고
{
popupUIDictionary.TryGetValue(uiName, out PopupUI popupUI); //딕셔너리에서 검색하여
popupUI.HidePanel(); //해당 PopupUI를 꺼줌
}
public void HideAllPanel()
{
if (currentPopupUI.Count <= 0) return;
var panelStackCopy = new Stack<PopupUI>(currentPopupUI);
foreach (var panel in panelStackCopy)
{
currentPopupUI.TryPop(out _);
panel.HidePanel();
}
}
public void MovePanel(string uiName, float x, float y, float fadeTime)
{
popupUIDictionary.TryGetValue(uiName, out PopupUI popupUI);
popupUI.MovePanel(x, y, fadeTime);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape)) //Esc눌러 가장 위에 띄워진 UI를 끄는 로직
{
if (currentPopupUI.Count > 0) //만약 Stack에 하나 이상의 PopupUI가 들어있다면 실행
{
string name = currentPopupUI.Peek().name;
bool isNotBattleResult = name != "DefeatUI" && name != "VictoryUI";
if (isNotBattleResult) //승리 시 UI와 패배 시 UI는 닫을 수 없게 설정
{
currentPopupUI.Peek().HidePanel(); //Stack의 가장 최근에 들어온 PopupUI를 닫아줌
}
}
}
}
}
먼저 PopupUI들을 담아 관리하는 Dictionary와 Stack을 갖습니다.
시작하면 씬에 하나 있는 Canvas를 찾고, 그 Canvas의 위치에서 PopupUI들을 모두 가져와 배열에 저장합니다.
PopupUI들이 저장된 배열을 반복문 돌리고, 중복 키가 검사되지 않은 PopupUI들을 모두 popupUIDictionary에 넣습니다.
ShowPanel 메서드
매개변수로 들어온 string값(UI이름)을 popupUIDictionary에 키값으로 넣어 Value값이 있는지 확인하고, 있다면 Value값(PopupUI)의 ShowPanel 메서드를 실행시켜 UI를 켜줍니다.
HidePanel 메서드
매개변수로 들어온 string값(UI이름)을 popupUIDictionary에 키값으로 넣어 Value값이 있는지 확인하고, 있다면 Value값(PopupUI)의 HidePanel 메서드를 실행시켜 UI를 꺼줍니다.
MovePanel 메서드
매개변수로 들어온 string값(UI이름)을 popupUIDictionary에 키값으로 넣어 Value값이 있는지 확인하고, 있다면 Value값(PopupUI)의 MovePanel 메서드를 실행시켜 UI를 움직입니다. 매개변수로 들어온 x, y 값을 전달합니다.
ESC 로직
( ESC눌러 가장 최근에 켜진, 가장 위에 있는 UI를 끄는 기능)
Esc키를 눌렀다면, 그리고 currentPopupUI 스택에 PopupUI가 하나 이상 들어있다면 currentPopupUI 스택의 맨 위에 저장된 PopupUI를 반환하고, 그 PopupUI를 꺼줍니다.
이 구조를 사용하여 만든 인게임 UI입니다.
끝
'Unity > 졸업작품' 카테고리의 다른 글
[Unity] 졸업작품<펭덤>의 전투 시스템 (1) | 2024.06.05 |
---|---|
[Unity] 졸업작품<펭덤>의 FSM 구조 - Reflection 이용한 FSM (0) | 2024.04.21 |