1.Onclick() 등록시 드래그 앤 드롭해서 연결하는 방식
-> 개선 : 이름만 건네주면 알아서 매핑해주는 것
2.다른 Text에 접근해서 바꾸고 싶은데
SerializeField를 사용해서 Text를 드래그 앤 드롭 방식으로 연결하는 방식
-> 개선 : 이것도 이름만 건네주면 알아서 매핑해주는 것
게임오브젝트의 이름을 가져와서 Bind에서 저장한다
Enum으로 버튼, 텍스트 등 게임오브젝트 종류에 따라 이름을 넣어둠
enum Buttons
{
PointButton,
}
enum TMP_Texts
{
PointText,
ScoreText,
}
enum값을 넘겨서 이름 겹치는게 있다면 알아서 저장하는걸 Bind에서 처리
enum값을 bind에 어떻게 전달할 것인가? -> 리플렉션을 사용해서 추출
private void Start()
{
Bind(typeof(Buttons));
Bind(typeof(TMP_Texts));
}
void Bind(Type type)
{
}
Type받아와서 추출한다
제네릭을 사용해서 버튼, 텍스트 컴포넌트를 가진 오브젝트를 찾아달라고한다.
private void Start()
{
Bind<Button>(typeof(Buttons));
Bind<TMP_Text>(typeof(TMP_Texts));
}
void Bind<T>(Type type)
{
}
저장은 딕셔너리로 저장한다
//딕셔너리로 받아올타입, 유니티엔진.오브젝트로 모든 오브젝트 저장가능
Dictionary<Type, UnityEngine.Object[]> _objects = new Dictionary<Type, UnityEngine.Object[]>();
void Bind<T>(Type type)
{
//Enum 이름을 string배열로 받아온다
string[] names = Enum.GetNames(type);
//딕셔너리에 넣기위해서 배열로 바꿔준다
UnityEngine.Object[] objects = new UnityEngine.Object[names.Length];
_objects.Add(typeof(T), objects);
}
자식을 찾아서 반환해준다
void Bind<T>(Type type) where T : UnityEngine.Object
{
//Enum 이름을 string배열로 받아온다
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++)
{
objects[i] = Util.FindChild<T>(gameObject, names[i], true);
}
}
//최상위 게임오브젝트, 이름(없으면 타입으로 찾음), 재귀적으로 찾을지(자식의 자식을 찾을지)
public static T FindChild<T>(GameObject go, string name = null, bool recursive = false) where T : UnityEngine.Object
{
//최상위 오브젝트가 null이면 리턴
if (go == null)
return null;
//직속 자식만 찾음
if(recursive == false)
{
for(int i = 0; i < go.transform.childCount; i++)
{
//직속 자식 찾는데 몇번째 자식 찾을지
Transform transform = go.transform.GetChild(i);
//이름이 비어있거나 내가 원하는 이름이면 반환
if (string.IsNullOrEmpty(name) || transform.name == name)
{
//transform에서 component를 받아와서
T component = transform.GetComponent<T>();
//컴포넌트가 null이 아니라면 반환
if (component != null)
return component;
}
}
}
//자식의 자식도 찾음
else
{
//GetComponentsInChildren 자식객체들을 찾음
foreach (T component in go.GetComponentsInChildren<T>())
{
//이름이 비어있거나 내가 원하는 이름이면 반환
if (string.IsNullOrEmpty(name) || component.name == name)
return component;
}
}
return null;
}
Bind로 저장된 게임오브젝트를 받아와서 사용한다
T Get<T>(int idx) where T : UnityEngine.Object
{
UnityEngine.Object[] objects = null;
//TryGetValue로 꺼내오는데 false라면 null반환
if (_objects.TryGetValue(typeof(T), out objects) == false)
return null;
return objects[idx] as T;
}
Get<TMP_Text>((int)TMP_Texts.ScoreText).text = "바인드테스트";
콜백방식으로 연동해줘야한다
Event System을 사용해서 UI와 연동하겠다
인터페이스로 받아서 인터페이스를 구현해준다
public class UI_EventHandler : MonoBehaviour, IBeginDragHandler, IDragHandler
{
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("OnBeginDrag");
}
public void OnDrag(PointerEventData eventData)
{
Debug.Log("OnDrag");
}
}
Image에만 UI_EventHandler 적용 후 사용
public void OnDrag(PointerEventData eventData)
{
transform.position = eventData.position;
Debug.Log("OnDrag");
}
public class UI_EventHandler : MonoBehaviour, IBeginDragHandler, IDragHandler
{
public Action<PointerEventData> OnBeginDragHandler = null;
public Action<PointerEventData> OnDragHandler = null;
public void OnBeginDrag(PointerEventData eventData)
{
if(OnBeginDragHandler != null)
OnBeginDragHandler.Invoke(eventData);
}
public void OnDrag(PointerEventData eventData)
{
if (OnDragHandler != null)
OnDragHandler.Invoke(eventData);
}
}
다른코드에서 콜백방식으로 연결해서 사용한다
Bind<Image>(typeof(Images));
//Image의 GameObject를 뽑아왔다가 다시 UI_EventHadnler를 뽑아옴
GameObject go = GetImage((int)Images.ItemIcon).gameObject;
UI_EventHandler evt = go.GetComponent<UI_EventHandler>();
evt.OnDragHandler += ((PointerEventData data) => { go.transform.position = data.position; });
//게임오브젝트, 함수를 받아올 Action, UIEvent(드래그, 클릭 ...)
public static void AddUIEvent(GameObject go, Action<PointerEventData> action, Define.UIEvent type = Define.UIEvent.Click)
{
UI_EventHandler evt = go.GetComponent<UI_EventHandler>();
if(evt == null)
evt = go.AddComponent<UI_EventHandler>();
evt.OnDragHandler += ((PointerEventData data) => { go.transform.position = data.position; });
}
여기서
UI_EventHandler evt = go.GetComponent<UI_EventHandler>();
if(evt == null)
evt = go.AddComponent<UI_EventHandler>();
이부분은 자주 나오기 때문에 Generic으로 함수를 따로 만든다
public static T GetorAddComponent<T>(GameObject go) where T : UnityEngine.Component
{
T component = go.GetComponent<T>();
if (component == null)
component = go.AddComponent<T>();
return component;
}
위와같이 만들면 기존의 코드를 아래와 같이 만들 수 있다
public static void AddUIEvent(GameObject go, Action<PointerEventData> action, Define.UIEvent type = Define.UIEvent.Click)
{
UI_EventHandler evt = Util.GetorAddComponent<UI_EventHandler>(go);
evt.OnDragHandler += ((PointerEventData data) => { go.transform.position = data.position; });
}
이벤트 연결하는 부분
public static void AddUIEvent(GameObject go, Action<PointerEventData> action, Define.UIEvent type = Define.UIEvent.Click)
{
UI_EventHandler evt = Util.GetorAddComponent<UI_EventHandler>(go);
switch(type)
{
case Define.UIEvent.Click:
evt.OnClickHandler -= action;
evt.OnClickHandler += action;
break;
case Define.UIEvent.Drag:
evt.OnDragHandler -= action;
evt.OnDragHandler += action;
break;
}
}
이벤트 일어나면 Invoke하는부분
public class UI_EventHandler : MonoBehaviour, IPointerClickHandler, IDragHandler
{
public Action<PointerEventData> OnClickHandler = null;
public Action<PointerEventData> OnDragHandler = null;
public void OnPointerClick(PointerEventData eventData)
{
if (OnClickHandler != null)
OnClickHandler.Invoke(eventData);
}
public void OnDrag(PointerEventData eventData)
{
if (OnDragHandler != null)
OnDragHandler.Invoke(eventData);
}
}
실제로 연결해서 사용하는 부분
GameObject go = GetImage((int)Images.ItemIcon).gameObject;
AddUIEvent(go, (PointerEventData data) => { go.transform.position = data.position; }, Define.UIEvent.Drag);
static을 사용해야한다
public static class Extension
{
public static void AddUIEvent(this GameObject go, Action<PointerEventData> action, Define.UIEvent type = Define.UIEvent.Click)
{
UI_Base.AddUIEvent(go, action, type);
}
}
이렇게 사용하면 여려줄로 사용하지 않고 한줄로 사용가능하다
GetButton((int)Buttons.PointButton).gameObject.AddUIEvent(OnButtonClicked);
public void OnButtonClicked(PointerEventData data)
{
_score++;
GetText((int)TMP_Texts.ScoreText).text = $"점수 :{_score}";
}