[Unity 강의] 뱀서라이크 강의 - UI 개선
카테고리: Class VamSurLike
태그: C#, Unity, VamSurLike
1. UI 개선
✔ UI 작업 - 시간이 많이 든다. -> 자동화
✔ UI 코드 추가
✔ 게임 결과 팝업창
2. 코드
🔹 UI_Base - 변경할 UI를 효율적으로 관리
🔹 UI_EventHandler - 해당하는 행동에 구독된 메서드를 실행
🔹 UI_GameResultPopup - 게임 결과 팝업창
🔹 Define - UIEvenet(Click, Drag 등 타입)
🔹 Extension - BindEvent (Button의 GameObject에 사용할 수 있는 BindEvent() 확장 메서드를 추가)
UI_Base
✅ Dictionary(_objects) 사용, 사용할 Type(text, Button ,~~)로 종류별로 []로 관리
✅ Bind - UI코드에서 Enum으로 분류된 UI 이름으로 된 정보를 가져와 _objects에 FindChild를 이용해 찾아서 추가한다.
✅ Get - 어떤 UI를 변경하고자 할 때 그 UI를 _objects에서 가져온다.
✅ BindEvent - EventHandler를 통해 Onclick, drag 등 이벤트를 적용한다.
UI_Base
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public abstract class UI_Base : MonoBehaviour
{
protected Dictionary<Type, UnityEngine.Object[]> _objects = new Dictionary<Type, UnityEngine.Object[]>();
protected bool _init = false;
public virtual bool Init()
{
if (_init)
return false;
_init = true;
return true;
}
private void Start()
{
Init();
}
protected void Bind<T>(Type type) where T : UnityEngine.Object
{
string[] names = Enum.GetNames(type);
UnityEngine.Object[] objects = new UnityEngine.Object[names.Length];
_objects.Add(typeof(T), objects);
for (int i = 0; i < names.Length; i++)
{
if (typeof(T) == typeof(GameObject))
objects[i] = Utils.FindChild(gameObject, names[i], true);
else
objects[i] = Utils.FindChild<T>(gameObject, names[i], true);
if (objects[i] == null)
Debug.Log($"Failed to bind({names[i]})");
}
}
protected void BindObject(Type type) { Bind<GameObject>(type); }
protected void BindImage(Type type) { Bind<Image>(type); }
protected void BindText(Type type) { Bind<TMP_Text>(type); }
protected void BindButton(Type type) { Bind<Button>(type); }
protected void BindToggle(Type type) { Bind<Toggle>(type); }
protected T Get<T>(int idx) where T : UnityEngine.Object
{
UnityEngine.Object[] objects = null;
if (_objects.TryGetValue(typeof(T), out objects) == false)
return null;
return objects[idx] as T;
}
protected GameObject GetObject(int idx) { return Get<GameObject>(idx); }
protected TMP_Text GetText(int idx) { return Get<TMP_Text>(idx); }
protected Button GetButton(int idx) { return Get<Button>(idx); }
protected Image GetImage(int idx) { return Get<Image>(idx); }
protected Toggle GetToggle(int idx) { return Get<Toggle>(idx); }
public static void BindEvent(GameObject go, Action action = null, Action<BaseEventData> dragAction = null, UIEvent type = UIEvent.Click)
{
UI_EventHandler evt = Utils.GetOrAddComponent<UI_EventHandler>(go);
switch (type)
{
case UIEvent.Click:
evt.OnClickHandler -= action;
evt.OnClickHandler += action;
break;
case UIEvent.Pressed:
evt.OnPressedHandler -= action;
evt.OnPressedHandler += action;
break;
case UIEvent.PointerDown:
evt.OnPointerDownHandler -= action;
evt.OnPointerDownHandler += action;
break;
case UIEvent.PointerUp:
evt.OnPointerUpHandler -= action;
evt.OnPointerUpHandler += action;
break;
case UIEvent.Drag:
evt.OnDragHandler -= dragAction;
evt.OnDragHandler += dragAction;
break;
case UIEvent.BeginDrag:
evt.OnBeginDragHandler -= dragAction;
evt.OnBeginDragHandler += dragAction;
break;
case UIEvent.EndDrag:
evt.OnEndDragHandler -= dragAction;
evt.OnEndDragHandler += dragAction;
break;
}
}
}
UI_EventHandler
✅ 해당하는 행동에 구독된 메서드를 실행
UI_EventHandler
public class UI_EventHandler : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler, IBeginDragHandler, IEndDragHandler
{
public Action OnClickHandler = null;
public Action OnPressedHandler = null;
public Action OnPointerDownHandler = null;
public Action OnPointerUpHandler = null;
public Action<BaseEventData> OnDragHandler = null;
public Action<BaseEventData> OnBeginDragHandler = null;
public Action<BaseEventData> OnEndDragHandler = null;
bool _pressed = false;
private void Update()
{
if (_pressed)
OnPressedHandler?.Invoke();
}
public void OnPointerClick(PointerEventData eventData)
{
if (OnClickHandler != null)
OnClickHandler.Invoke();
}
public void OnPointerDown(PointerEventData eventData)
{
_pressed = true;
OnPointerDownHandler?.Invoke();
}
public void OnPointerUp(PointerEventData eventData)
{
_pressed = true;
OnPointerUpHandler?.Invoke();
}
public void OnDrag(PointerEventData eventData)
{
_pressed = true;
OnDragHandler?.Invoke(eventData);
}
public void OnBeginDrag(PointerEventData eventData)
{
OnBeginDragHandler?.Invoke(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
OnEndDragHandler?.Invoke(eventData);
}
}
UI_GameResultPopup
✔ 게임 결과 팝업창
✅ Enum 수정이 필요한 UI들을 Tpye(Text, Button ~~) 별로 구분해서 UI 이름과 같게 설정
✅ init - Bind() Type별로 UI_Base-Dictionary(_object)에 담아서 관리
✅ init - GetButton().BindEvent - 버튼에 OnClickButton 이벤트 추가
✅ RefreshUI - UI 정보 갱신
UI_GameResultPopup
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
public class UI_GameResultPopup : UI_Base
{
#region UI 기능 리스트
// 정보 갱신
// ResultStageValueText : 해당 스테이지 수
// ResultSurvivalTimeValueText : 스테이지 클리어 까지 걸린 시간 ( mm:ss 로 표기)
// ResultGoldValueText : 죽기전 까지 얻은 골드
// ResultKillValueText : 죽기전 까지 킬 수
// ResultRewardScrollContentObject : : 보상으로 얻게될 아이템이 들어갈 부모 개체
// (골드, 경헌치, 아이템, 캐릭터 강화석 등을 보상으로)
// 로컬라이징 텍스트
// GameResultPopupTitleText
// ResultSurvivalTimeText
// ConfirmButtonText
#endregion
enum GameObjects
{
ContentObject,
ResultRewardScrollContentObject,
ResultGoldObject,
ResultKillObject,
}
enum Texts
{
GameResultPopupTitleText,
ResultStageValueText,
ResultSurvivalTimeText,
ResultSurvivalTimeValueText,
ResultGoldValueText,
ResultKillValueText,
ConfirmButtonText,
}
enum Buttons
{
StatisticsButton,
ConfirmButton,
}
public override bool Init()
{
if (base.Init() == false)
return false;
BindObject(typeof(GameObjects));
BindText(typeof(Texts));
BindButton(typeof(Buttons));
GetButton((int)Buttons.StatisticsButton).gameObject.BindEvent(OnClickStatisticsButton);
GetButton((int)Buttons.ConfirmButton).gameObject.BindEvent(OnClickConfirmButton);
RefreshUI();
return true;
}
public void SetInfo()
{
RefreshUI();
}
void RefreshUI()
{
if (_init == false)
return;
// 정보 취합
GetText((int)Texts.GameResultPopupTitleText).text = "Game Result";
GetText((int)Texts.ResultStageValueText).text = "4 STAGE";
GetText((int)Texts.ResultSurvivalTimeText).text = "Survival Time";
GetText((int)Texts.ResultSurvivalTimeValueText).text = "14:23";
GetText((int)Texts.ResultGoldValueText).text = "200";
GetText((int)Texts.ResultKillValueText).text = "100";
GetText((int)Texts.ConfirmButtonText).text = "OK";
}
void OnClickStatisticsButton()
{
Debug.Log("OnClickStatisticsButton");
}
void OnClickConfirmButton()
{
Debug.Log("OnClickConfirmButton");
}
}
Define, Extension
✔ 마우스의 행동들 Enum으로 선언
✅ BindEvent (Button의 GameObject에 사용할 수 있는 BindEvent() 확장 메서드를 추가)
🔹 Define - UIEvenet(Click, Drag 등 타입)
🔹 Extension - BindEvent (Button의 gameobject에 BindEvent() 확장 메서드 추가 )
Define, Extension
public enum UIEvent
{
Click,
Pressed,
PointerDown,
PointerUp,
Drag,
BeginDrag,
EndDrag,
}
public static class Extension
{
public static void BindEvent(this GameObject go, Action action = null, Action<BaseEventData> dragAction = null, UIEvent type = UIEvent.Click)
{
UI_Base.BindEvent(go, action, dragAction, type);
}
}
이것저것 메모
UI 관리, UI 이름 변경 시 수정
button, texts 등 enum 을 이용해 사용할 오브젝트의 name을 똑같이 선언
ui 이름이 변경되면 수정 필요
UI 테스트 시 주의
버튼 테스트 시 RayCast, EventSystem 확인
잡담, 일기?
이전 프로젝트에선 다 드래그 앤 드롭으로 UI를 만들었었는데
이 방법으로 하면 더 빨리 작업을 끝낼 수 있을 거 같다.
댓글남기기