FrameWork
[Unity] 프레임 워크 개발 - UI Manager( v.0.1.1 )
EveryDay.DevUp
2020. 7. 8. 00:01
▣ 기본 설정
https://everyday-devup.tistory.com/44
▣ 기반 지식
https://everyday-devup.tistory.com/54
UIManager는 Stack으로 UI를 관리하는 기능을 하며, UI을 추가/삭제/업데이트의 기능을 담당한다. 모든 UI는 UIBase 클래스를 상속받아서 구현하여 UIBase로 관리된다.
using UnityEngine;
using UnityEngine.AddressableAssets;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace EverydayDevup.Framework
{
/// <summary>
/// UI를 관리하는 클래스
/// </summary>
public class UIManager : MonoBehaviour, IManager
{
/// <summary>
/// 게임에서 사용하는 모든 UI가 정의된 ScriptableObject
/// </summary>
public UIAddressable uIAddressable;
/// <summary>
/// UI의 최상위 Canvas로, 해당 Canvas하위에 UI가 생성되고 삭제됨
/// </summary>
public Canvas rootCanvas;
/// <summary>
/// 현재 생성되고 있는 UI 정보
/// </summary>
UIAddressableData currentData = null;
/// <summary>
/// UI를 순서에 따라 순차적으로 생성하기 위해 Queue로 관리
/// </summary>
Queue<int> requestUIQueue = new Queue<int>();
/// <summary>
/// 현재 화면에 보여지는 UI를 Stack으로 관리
/// </summary>
Stack<UIBase> uiBases = new Stack<UIBase>();
/// <summary>
/// 화면에 새로운 UI를 추가하기 위해 호출되는 함수
/// </summary>
/// <param name="type">추가할 UI의 type</param>
public void Push(UI_TYPE type)
{
requestUIQueue.Enqueue( (int)type );
}
/// <summary>
/// 매 프레임마다 현재 UI를 업데이트하며, 추가할 ui가 있는 경우 ResourceManager에서 읽어옴
/// </summary>
public void ManagerUpdate()
{
UIBase uiBase;
UIBase[] uiBaseArray = uiBases.ToArray();
for( int i = 0, icount = uiBaseArray.Length; i < icount; ++i )
{
uiBase = uiBaseArray[i];
if( uiBase != null )
{
uiBase.UIUpdate();
}
}
/// 추가할 UI가 있는 경우
if( requestUIQueue != null && requestUIQueue.Count > 0 )
{
///현재 로드 중인 UI가 없는 경우
if( currentData == null )
{
UI_TYPE requestUI = (UI_TYPE)requestUIQueue.Dequeue();
OnLoad( requestUI );
}
}
}
/// <summary>
/// 매 초마다 현재 UI를 업데이트
/// </summary>
public void ManagerLoop()
{
UIBase uiBase;
UIBase[] uiBaseArray = uiBases.ToArray();
for( int i = 0, icount = uiBaseArray.Length; i < icount; ++i )
{
uiBase = uiBaseArray[i];
if( uiBase != null )
{
uiBase.UILoop();
}
}
}
/// <summary>
/// UI를 불러오는 함수
/// </summary>
/// <param name="type"></param>
void OnLoad(UI_TYPE type)
{
/// 정의된 UI 데이터로 부터 추가할 UI 데이터를 얻은 후 리소스 매니저를 통해 로드
currentData = uIAddressable.GetUIAddressableDataByUIType( type );
Game.Instance.resourcesManager.Load( currentData.reference, rootCanvas.transform, OnComplete, OnFail );
}
/// <summary>
/// UI가 정상적으로 로드 되었을 때 불리는 함수
/// </summary>
/// <param name="go"></param>
void OnComplete(GameObject go)
{
/// UI의 기본 좌표 및 앵커 값을 수정
RectTransform rectTransform = go.GetComponent<RectTransform>();
rectTransform.SetParent( rootCanvas.transform );
rectTransform.offsetMax = Vector2.zero;
rectTransform.offsetMin = Vector2.zero;
rectTransform.localPosition = Vector3.zero;
/// 추가된 UI가 전체화면인 경우 기존 UI에서 전체화면인 UI를 비활성화
UIBase addUI = go.GetComponent<UIBase>();
/// 추가된 UI 설정
addUI.UIInit( currentData, uiBases.Count );
addUI.UIActive();
addUI.UIUpdate();
if( addUI.IsFullScreen )
{
UIBase[] uiBaseArray = uiBases.ToArray();
UIBase uiBase;
for( int i = uiBaseArray.Length - 1; i >= 0; --i )
{
uiBase = uiBaseArray[i];
if( uiBase.IsFullScreen )
{
uiBase.UIInActive();
break;
}
}
}
/// 추가된 UI를 Stack에 추가
uiBases.Push( addUI );
currentData = null;
}
/// <summary>
/// UI 로드에 실패할 경우 호출되는 함수
/// </summary>
void OnFail()
{
currentData = null;
}
/// <summary>
/// UI를 삭제할 때 호출되는 함수
/// </summary>
public void Pop(UI_TYPE type)
{
if( uiBases.Count <= 0 )
{
return;
}
/// Pop할 UI_TYPE을 찾기 위해 임시 Stack을 설정
Stack<UIBase> tempStack = new Stack<UIBase>();
UIBase uiBase;
while( uiBases.Count > 0 )
{
uiBase = uiBases.Pop();
if( uiBase.type == type )
{
/// pop하는 UI가 전체 화면이였던 경우, 남은 UI중 전체화면인 가장 상단의 UI를 활성화
if( uiBase.IsFullScreen )
{
UIBase otherUIBase;
UIBase[] uiBaseArray = uiBases.ToArray();
for( int i = uiBaseArray.Length - 1; i >= 0; --i )
{
otherUIBase = uiBaseArray[i];
if( otherUIBase.IsFullScreen )
{
otherUIBase.UIActive();
break;
}
}
}
Game.Instance.resourcesManager.UnLoad( uiBase.AssetReference, uiBase.gameObject, uiBase.IsCaching );
uiBase.UIClear();
}
else
{
tempStack.Push( uiBase );
}
}
// 기존 UI의 순서를 맞추기 위해 tempStack을 다시 옮김
while( tempStack.Count > 0 )
{
uiBase = tempStack.Pop();
uiBases.Push( uiBase );
}
}
/// <summary>
/// 현재 ui를 모두 지움
/// </summary>
public void ManagerClear()
{
UIBase baseUI;
while( uiBases.Count > 0 )
{
baseUI = uiBases.Pop();
if( baseUI != null )
{
baseUI.UIClear();
}
}
}
}
}
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace EverydayDevup.Framework
{
/// <summary>
/// UI가 기본적으로 상속받는 클래스
/// </summary>
[RequireComponent( typeof( Canvas ))]
public class UIBase : MonoBehaviour, IBase
{
/// <summary>
/// UI 데이터를 가지고 있는 ScriptableObject
/// </summary>
UIAddressableData uiAddressableData;
/// <summary>
/// UI의 최상위에 있는 Canvas
/// </summary>
Canvas canvas;
/// <summary>
/// 해당 UI가 전체 화면인지 체크
/// </summary>
public bool IsFullScreen
{
get
{
return uiAddressableData.isFullScreen;
}
}
/// <summary>
/// 해당 UI가 캐싱되는지 체크
/// </summary>
public bool IsCaching
{
get
{
return uiAddressableData.isCaching;
}
}
/// <summary>
/// 해당 UI의 Addressable 주소
/// </summary>
public AssetReference AssetReference
{
get
{
return uiAddressableData.reference;
}
}
/// <summary>
/// 해당 UI의 UI_TYPE
/// </summary>
public UI_TYPE type
{
get
{
return uiAddressableData.type;
}
}
/// <summary>
/// UI가 로드 되었을 때 불리는 초기화 함수
/// </summary>
/// <param name="uiAddressableData"> UI 정보</param>
/// <param name="stackCount"> UI의 스택 위치 </param>
public void UIInit(UIAddressableData uiAddressableData, int stackCount )
{
this.uiAddressableData = uiAddressableData;
canvas = GetComponent<Canvas>();
canvas.overrideSorting = true;
canvas.sortingOrder = stackCount;
OnInit();
}
/// <summary>
/// UI가 활성화 되면 불리는 함수
/// </summary>
public void UIActive()
{
canvas.enabled = true;
OnActive();
}
/// <summary>
/// UI가 비활성화 되면 불리는 함수
/// </summary>
public void UIInActive()
{
canvas.enabled = false;
OnInActive();
}
/// <summary>
/// UI가 업데이트 될때마다 호출되는 함수
/// </summary>
public void UIUpdate()
{
if( canvas.enabled == false )
{
return;
}
OnUpdate();
}
/// <summary>
/// 매 초마다 호출되는 함수
/// </summary>
public void UILoop()
{
if( canvas.enabled == false )
{
return;
}
OnLoop();
}
/// <summary>
/// UI가 사라질 때 호출 되는 함수
/// </summary>
public void UIClear()
{
OnClear();
uiAddressableData = null;
GameObject.Destroy( gameObject, 0f );
}
public virtual void OnInit() {}
public virtual void OnActive() {}
public virtual void OnInActive() {}
public virtual void OnUpdate() {}
public virtual void OnLoop() {}
public virtual void OnClear() {}
}
}
▣ 사용 가이드
: UILoading을 구현하여, Game.cs에서 Start에서 로고 UI를 출력할 수 있음
using UnityEngine;
using EverydayDevup.Framework;
public class UILoading : UIBase
{
public UIReference refData;
public override void OnInit()
{
base.OnInit();
TMPro.TMP_Text text = refData.GetData<TMPro.TMP_Text>( "company_name" );
text.SetText( "EveryDay.DevUp" );
}
}
public class Game : Singleton<Game>
{
protected virtual void Start()
{
uiManager.Push( UI_TYPE.LOADING );
}
}