반응형
인프런 루키스님의 강좌를 참고했습니다.
- 드래그 앤 드롭을 매번 해주는 것을 대체하기 위해서 구현했다.
- dictionary에 사용할 오브젝트를 담고, 드래그 앤 드롭 대신 enum을 이용했다.
- enum의 원소이름은 사용할 오브젝트의 실제 이름과 동일해야 한다.
// UI_Base
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class UI_Base : MonoBehaviour
{
// 컴포넌트 타입별 오브젝트들을 담아 관리할 딕셔너리
// Key : enum 이름, Value : 오브젝트 배열
Dictionary<Type, UnityEngine.Object[]> dict = new Dictionary<Type, UnityEngine.Object[]>();
// enum의 원소이름을 바인딩할 자식 오브젝트의 이름과 같게 세팅한 뒤
// binding 함수를 이용해 해당 오브젝트를 바인딩시킴
// ComponentName는 연결할 오브젝트의 컴포넌트의 이름을 뜻함.
// ComponentName은 UnityEngine.Object를 상속받은 컴포넌트여야함.
protected void Binding<ComponentName>(Type enumType) where ComponentName : UnityEngine.Object
{
// 1. Reflect를 이용해 enum의 원소들을 가져옴
// names는 enum의 원소들의 이름
string[] names = enumType.GetEnumNames();
// 2. 해당 원소들의 갯수만큼 오브젝트를 찾아서 바인딩 할 것임.
// 따라서 해당 오브젝트들을 담을 배열을 해당 원소들의 갯수만큼의 크기를 가진 배열로 선언
UnityEngine.Object[] objs = new UnityEngine.Object[names.Length];
// 3.이 오브젝트의 자식 오브젝트 중에서 앞서 찾은 names와 같은 이름인 오브젝트들을 찾아
// 오브젝트 배열에 담음
for (int i = 0; i < names.Length; i++)
{
// enum의 이름을 이용해 자식들 중에서 같은 이름의 오브젝트를 찾고,
// 해당 오브젝트를 오브젝트 배열에 담음
// 찾을 오브젝트의 타입이 GameObject인 경우 -> GameObject 타입은 Component로 인식하지 못하므로 따로 처리해주어야 함!
if (typeof(ComponentName) == typeof(GameObject))
objs[i] = Util.FindComponentInChild(this.gameObject, names[i], true);
else
objs[i] = Util.FindComponentInChild<ComponentName>(this.gameObject, names[i], true);
// 바인딩에 실패한 경우
if (objs[i] == null)
Debug.LogError($"바인딩 실패 {names[i]}");
}
// 위에서 담은 오브젝트 배열을 딕셔너리에 담음
dict.Add(typeof(ComponentName), objs);
}
// TODO
// Get 함수를 이용해 딕셔너리에 바인딩된 오브젝트의 해당되는 컴포넌트를 가져옴
// 매개변수 index는 enum의 이름을 이용한 인덱스를 의미
protected ComponentName Get<ComponentName>(int enumNameToInt) where ComponentName : UnityEngine.Object
{
UnityEngine.Object[] objs = null;
// 딕셔너리의 key값을 이용해 value를 가져옴
// enum의 이름을 이용해 해당 enum이름의 컴포넌트를 가진 오브젝트들의 배열을 가져옴
// 오브젝트 배열을 가져오지 못한 경우 -> 리턴
if (dict.TryGetValue(typeof(ComponentName), out objs) == false)
return null;
// 오브젝트의 컴포넌트를 리턴
// as : UnityEngine.Object 타입인 objs[index]를 ComponentName으로 캐스팅해줌
// objs[index]는 원래 ComponentName 컴포넌트를 가진 오브젝트인데,
// 현재 범용성을 위해 UnityEngine으로 업캐스팅된 상태이므로, as를 이용해 다시 원래대로 놓는 것임.
return objs[enumNameToInt] as ComponentName;
}
// 자주 사용될 컴포넌트의 타입들을 제네릭 선언없이 간편하게 사용하기 위해 선언
protected GameObject GetGameObject(int enumNameToInt) { return Get<GameObject>(enumNameToInt); }
protected Button GetButton(int enumNameToInt) { return Get<Button>(enumNameToInt); }
protected Text GetText(int enumNameToInt) { return Get<Text>(enumNameToInt); }
protected TMP_Text GetTMP(int enumNameToInt) { return Get<TMP_Text>(enumNameToInt); }
protected Image GetImage(int enumNameToInt) { return Get<Image>(enumNameToInt); }
}
// Util
using UnityEngine;
public class Util : MonoBehaviour
{
// root : 자식을 찾을 가장 상위의 오브젝트, root의 자식들을 검색
// name : 찾을 이름, 이름이 null인 경우는 ComponeneName라는 이름의 컴포넌트를 가졌으면 리턴
// recursive : 재귀적으로 찾을 건지 여부, 자식들의 자식들까지 찾을 건지, 직속 자식들 중에서만 찾을 건지 선택
// 리턴 : 찾은 컴포넌트 이름을 리턴
public static ComponentName FindComponentInChild<ComponentName>(GameObject root, string name = null, bool recursive = false) where ComponentName : UnityEngine.Object
{
// root의 자식들을 찾아야 하는데 root가 없는 경우 -> 리턴
if (root == null)
return null;
// 직속 자식들 중에서만 찾는 경우
if (recursive == false)
{
// 직속자식의 수를 나타내는 childCount를 이용하여 반복문 진행
for (int i = 0; i < root.transform.childCount; i++)
{
// 모든 오브젝트는 Transform을 가짐
// 따라서 Transform을 이용해 직속자식에 접근할 수 있음
// child는 직속자식을 의미함.
Transform child = root.transform.GetChild(i);
// 입력받은 이름이 없는 경우 -> 그냥 같은 컴포넌트를 찾기만 하면 바로 리턴
// 입력받은 이름이 있는 경우 -> 이름이 같을 경우에만 리턴
// 따라서 입력받은 이름이 없거나, 이름이 같은 경우에 해당 컴포넌트를 리턴
if (string.IsNullOrEmpty(name) || name == child.name)
{
// child에서 ComponentName 컴포넌트를 찾음
ComponentName component = child.GetComponent<ComponentName>(); // -> 여기서 GameObject를 찾지못함 1
// child가 ComponentName 컴포넌트를 가진 경우에만 동작
// 해당 컴포넌트 리턴
if (component != null)
return component;
}
}
}
// 모든 자식들에 대해 찾는 경우
else
{
// ComponentName 컴포넌트를 모두 찾아서 담음
ComponentName[] components = root.GetComponentsInChildren<ComponentName>(); // -> 여기서 GameObject를 찾지못함 2
// 담은 컴포넌트들을 돌면서 찾음
foreach (ComponentName component in components)
{
if (string.IsNullOrEmpty(name) || name == component.name)
return component;
}
}
return null;
}
public static GameObject FindComponentInChild(GameObject root, string name = null, bool recursive = false)
{
// GameObject는 Compnent를 상속받지 않아서 유니티 내부의 GetComponent를 통해 찾을 수 없음
// 따라서 GameObject가 항상 가지고 있는 Transform으로 찾아서 판단.
Transform transform = FindComponentInChild<Transform>(root, name, recursive);
if (transform != null)
return transform.gameObject;
return null;
}
}
//UI_Scene : 사용 예시
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class UI_Scene : UI_Base
{
enum GameObjects
{
// 게임 오브젝트의 이름들...
Lee,
Eun,
Chan,
}
enum Texts
{
// 텍스트 이름들...
Lee,
}
private void Start()
{
Binding<TMP_Text>(typeof(Texts));
Binding<GameObject>(typeof(GameObjects));
//Get<GameObject>((int)GameObjects.Lee);
Get<Text>((int)Texts.Lee);
GetTMP((int)Texts.Lee).text = "hi";
}
}