이번 과제는 일정, 컨디션 관리 대폭망이었다.
과제의 완성은 체념하고 "UI관리법 터득하기"를 나만의 목표로 세웠다.
결과는 필수구현만 개발한 뉴비의 깃허브..
전에 같은 조였던 분의 도움을 받아 UI를 프리팹으로 두고 필요할 떄 객체화 해서 사용하는 방법을 알게됐다.
우선 이름이 동일한 스크립트와 프리팹을 준비한다.
public static class Util
{
public static GameObject Instantiate<T>(string path = null, Transform parent = null) where T : UIBase
{
if (string.IsNullOrEmpty(path))
path = "Prefabs";
string fileName = typeof(T).Name;
GameObject prefab = Resources.Load<T>($"{path}/{fileName}").gameObject;
GameObject go = Object.Instantiate(prefab, parent);
go.name = fileName;
return go;
}
,,,생략,,,
}
이 함수는 UI에 있는 버튼이나 텍스트 등을 가져와 사용하기 위해 구현했다.
public static class Util
{
,,,생략,,,
// 이름으로 자식 가져오기
public static T FindChild<T>(GameObject go, string componentName, bool isRecur = false) where T : UnityEngine.Object
{
if (go == null) return null;
if(!isRecur)
{
for (int i = 0; i < go.transform.childCount; ++i)
{
Transform transform = go.transform.GetChild(i);
if (transform.name == componentName)
{
T component = transform.GetComponent<T>();
if (component != null)
return component;
}
}
}
else
{
foreach(T component in go.GetComponentsInChildren<T>())
{
if(componentName == component.name)
return component;
}
}
return null;
}
}
T가 Button인 것으로 예를 들어본다.
컴포넌트의 이름을 받고 부모 오브젝트의 자식들을 전부 순회하며 이름과 동일한지 판단한다.
이름과 동일하면 Button을 반환한다.
자식의 자식의 (그 이상 포함) 오브젝트까지 찾아봐야 할 경우에는 그냥 GetComponentsInChildren
메서드를 사용한다.
public class UIManager : MonoBehaviour
{
GameObject UI_Title;
,,,비슷한 내용 생략,,,
public GameObject[] Popups = new GameObject[(int)POPUP.INPUT_ERROR+1];
// Start is called before the first frame update
void Awake()
{
Init();
}
void Init()
{
UI_Title = Util.Instantiate<UI_Title>("Prefabs/UI", GameObject.Find("Canvas").transform);
,,,비슷한 내용 생략,,,
}
,,,생략,,,
}
Util
의 Instantiate
로 타입명 UI_Title
와 이름이 동일한 프리팹을 객체화 시킨다.
public class UI_Deposit : UIBase
{
private UIManager UI;
private ATM ATM;
[SerializeField] private Button ten;
,,,비슷한 내용 생략,,,
private void Start()
{
UI = ATM.instance.UIManager;
ATM = ATM.instance;
// 가져오기
ten = Util.FindChild<Button>(gameObject, "Ten");
,,,비슷한 내용 생략,,,
// 이벤트 등록
ten.onClick.AddListener(RequestDepositTen);
,,,비슷한 내용 생략,,,
}
,,,생략,,,
}
현재UI_Deposit.cs
스크립트는 UI_Deposit
에 붙어있는 상태다.
Util
의 FindChild
로 Ten
이라는 이름의 Button
을 가져와서 사용한다.
오늘 진행된 특강세션에서는 제네릭을 이용한 싱글톤에 대한 내용을 다루었다.
바로 코드.
public class SingletoneBase : MonoBehaviour
{
public static SingletoneBase instance;
void Awake()
{
intance = this;
}
}
============================================================
public class GameManager : SingletoneBase
{
public void Something(){}
}
============================================================
public class AudioManager : SingletoneBase
{
GameManager.instance.Something(); // 불가능!!!
}
싱글톤이 여러개가 필요할 때 이런 식으로 부모가 되는 싱글톤이 있으면 코드의 반복이 줄어든다.
하지만 위 코드에는 문제점이 있는데, 바로 AudioManager
에서 싱글톤인 GameManager
의 멤버에는 접근할 수 없다는 것.
간단하게 말하면 맨 위의 싱글톤 베이스에서 instance
의 타입은 SingletoneBase
이다.
싱글톤 베이스를 상속받은 GameManager
부분은 접근하지 못하는 것이 당연하다.
이를 해결하기 위해서 제네릭을 사용한다.
public class SingletoneBase<T> : MonoBehaviour where T : MonoBehaviour
{
// 프로퍼티
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
string typeName = typeof(T).FullName;
GameObject go = new GameObject(typeName);
_instance = go.AddComponent<T>();
DontDestroyOnLoad(go);
}
return _instance;
}
}
void Awake()
{
Init();
}
public virtual void Init()
{
Debug.Log(transform.name + " is Init");
}
}
============================================================
public class GameManager : SingletoneBase<GameManager>
{
public void Something(){}
}
제네릭을 사용해서 _instance
의 타입을 지정해주면 SingletoneBase
를 상속받은 자식들의 영역에도 접근할 수 있게된다.
그리고 프로퍼티 부분은 싱글톤의 오류를 방지하기 위한 코드가 적혀있다.(디버그 찍어봐야겠다.)