1. Bind에 Gameobject도 추가하기
01 enum 추가
enum GameObjects
{
TestObject,
}
GameObject type의 Bind도 받고 싶다고 추가
Bind<GameObject>(typeof(GameObjects));
02 T의 타입이 GameObject인지 확인
if(typeof(T) == typeof(GameObject))
objects[i] = Util.FindChild(gameObject, names[i], true);
else
objects[i] = Util.FindChild<T>(gameObject, names[i], true);
if (objects[i] == null)
Debug.Log($"Faild to bind!{names[i]}");
03 GameObject반환
우리가 이전에 썼단 FindChild에서는 GameObject를 반환하지 못하므로 GameObject자체 반환을 위한 FindChild를 작성한다.
public static GameObject FindChild(GameObject go, string name = null, bool recursive = false)
{
Transform transform = FindChild<Transform>(go, name, recursive);
if (transform == null)
return null;
return transform.gameObject;
}
04 반복적으로 쓰기 귀찮으니까
Text GetText(int idx){return Get<Text>(idx);}
Button GetButton(int idx){return Get<Button>(idx);}
Image GetImage(int idx){return Get<Image>(idx);}
아래와 같이 사용할 수 있다.
//Get<Text>((int)Texts.scoreText).text = "BindText";
GetText((int)Texts.scoreText).text = "BindText";
3. UI_Base 만들기
위의 Bind와 Get같은 경우 모든 UI에서 사용된다. 그러므로 공용적으로 사용될 수 있게 베이스클래스를 만드는 것이 좋다.
public class UI_Base : MonoBehaviour
{
Dictionary<Type, UnityEngine.Object[]> _objects = new Dictionary<Type, UnityEngine.Object[]>();
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] = Util.FindChild(gameObject, names[i], true);
else
objects[i] = Util.FindChild<T>(gameObject, names[i], true);
if (objects[i] == null)
Debug.Log($"Faild to bind!{names[i]}");
}
}
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;
}
Text GetText(int idx) { return Get<Text>(idx); }
Button GetButton(int idx) { return Get<Button>(idx); }
Image GetImage(int idx) { return Get<Image>(idx); }
}
01 UI_Button 상속
public class UI_Buttons : UI_Base
02 보호수준
UI_Base를 상속하고나면 보호수준 관련에러가 뜨는 것을 확인할 수 있다.
이러한 문제들을 해결하기 위해 private로 막아주는 것이 아니라 protected르로 자손들이 사용할 수 있게 변경해주면 된다.
protected void Bind<T>(Type type) where T : UnityEngine.Object
protected T Get<T>(int idx) where T : UnityEngine.Object
protected Text GetText(int idx) { return Get<Text>(idx); }
protected Button GetButton(int idx) { return Get<Button>(idx); }
protected Image GetImage(int idx) { return Get<Image>(idx); }
다시 되돌아가보면 문제가 해결됐음을 확인할 수 있다.
UI와 Event연동
01 Unity 이벤트 시스템 활용
public class UI_EventHandler : MonoBehaviour, IDragHandler
{
public void OnDrag(PointerEventData eventData)
{
transform.position = eventData.position;
}
}
02 직접 이벤트 처리 로직을 제어
public class UI_EventHandler : MonoBehaviour, IDragHandler
{
Action<PointerEventData> onDragHandler = null;
public void OnDrag(PointerEventData eventData)
{
transform.position = eventData.position;
if (onDragHandler != null)
onDragHandler.Invoke(eventData);
}
}
이미지를 사용할거니까 Images enum추가
enum Images
{
ItemIcon,
}
이미지를 받아오지 않고 게임 오브젝트로 한 번 더 이렇게 뽑아오는 이유
private void Start()
{
GameObject go = GetImage((int)Images.ItemIcon).gameObject;
UI_EventHandler evt = go.GetComponent<UI_EventHandler>();
}
이미지 컴포넌트를 가진 게임 오브젝트를 얻고 이는 이미지 컴포넌트가 게임 오브젝트에 부착되어 있으므로 이미지 컴포넌트가 속한 게임 오브젝트에 접근하기 위해 사용된다. 그리고 게임 오브젝트에 연결된 UI_EventHandler컴포넌트를 가져온다.
evt.onDragHandler += ((PointerEventData data) => { transform.position = data.position; });
이렇게 하면 Image가 움직이지 않는다. 왜냐하면 UI_Buttons는 UI_Button에 붙어있기 때문이다. 그래서 Image를 이동시키기 위해서는 아래와 같이 변경해야 한다.
evt.onDragHandler += ((PointerEventData data) => { evt.gameObject.transform.position = data.position; });
BindEvent
01 AddUIEvent
public static void AddUIEvent(GameObject go, Action<PointerEventData> action, Define.UIEvent type = Define.UIEvent.Click)
{
Util.GetOrAddComponent<UI_EventHandler>(go);
UI_EventHandler evt = go.GetComponent<UI_EventHandler>();
if (evt == null)
evt = go.GetComponent<UI_EventHandler>();
evt.onDragHandler += ((PointerEventData data) => { evt.gameObject.transform.position = data.position; });
}
매개변수
go : UI이벤트를 추가할 GameObject
action : UI 이벤트 발생시 실행할 액션(델리게이트)이다. PointerEventData를 입력 매개변수로 받아 UI이벤트 핸들링을 수행한다.
type : UI 이벤트 유형을 지정하는 열거형 매개변수이다. 기본값은 Define.UIEvent.Click으로 설정되어있다.
02 GetOrAddComponent
주어진 GameObject에서 컴포넌트를 찾거나 컴포넌트가 없는 경우에는 새로운 컴포넌트를 추가하여 반환
Util.cs
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;
}
위의 메서드를 통해
Util.GetOrAddComponent<UI_EventHandler>(go);
UI_EventHandler evt = go.GetComponent<UI_EventHandler>();
위의 코드를 아래와 같이 한줄로 표현할 수 있다.
UI_EventHandler evt = Util.GetOrAddComponent<UI_EventHandler>(go);
03 UI_EventHandler컴포넌트의 이벤트 핸들러에 대한 subscribe, unsubscribe설정
switch(type)
{
case Define.UIEvent.Click:
evt.onClickHandler -= action;
evt.onClickHandler += action;
break;
case Define.UIEvent.Drag:
evt.onDragHandler -= action;
evt.onDragHandler += action;
break;
}
코드 전문
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;
}
}
04 AddUIEvent 사용
UIButtons.cs에서 코드 변경
GameObject go = GetImage((int)Images.ItemIcon).gameObject;
UI_EventHandler evt = go.GetComponent<UI_EventHandler>();
evt.onDragHandler += ((PointerEventData data) => { evt.gameObject.transform.position = data.position; });
위의 코드를 아래와 같이 변경
아래와 같이 변경할 경우 이벤트 핸들러 추가 코드가 더 간결하고 중복을 줄일 수 있으며 코드 재사용에 용이하다.
GameObject go = GetImage((int)Images.ItemIcon).gameObject;
AddUIEvent(go, (PointerEventData data) => { go.transform.position = data.position; }, Define.UIEvent.Drag);
Extension Method
아래와 같이 바로 Event를 연결할 수 있는 방법은 없을까?
GameObject go = GetImage((int)Images.ItemIcon).gameObject.AddUIEvent();
C#에서 기존의 클래스나 인터페이스에 새로운 메서드를 추가하는 방법 중 하나이다. Unity의 GameObject클래스는 수정할 수 없는 외부 라이브러리에서 제공되는 클래스이기때문에 직접 코드를 추가할 수 없지만 C# Extension Method를 통해 기존클래스에 새로운 메서드를 추가할 수 있다.
확장 메서드 특징
1) 정적 클래스 내부에 정의된다.
2) 첫번째 매개변수로 this를 사용하여 어떤 클래스나 인터페이스의 인터페이스의 확장 메서드인지 명시한다.
3) 이는 모든 인스턴스에 사용할 수 있다.
예를 들어 AddUIEvent라는 확장 메서드는 GameObject클래스에 새로운 메서드처럼 추가되었다. 그러면 GameObject인스턴스에서 AddUIEvent 메서드를 호출할 수 있게 된다.
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으로 얻은 버튼 게임 오브젝트에 대해 AddUIEvent확장 메서드를 호출하여 이벤트를 연결한다.
GetButton((int)Buttons.PointButton).gameObject.AddUIEvent(OnButtonClicked);
참고 : 본 내용은 MMORPG PART3 강의를 수강하여 작성하였습니다.
https://www.inflearn.com/course/mmorpg-%EC%9C%A0%EB%8B%88%ED%8B%B0/dashboard
'유니티 공부 > Unity' 카테고리의 다른 글
Unity- Update vs FixedUpdate vs LateUpdate (0) | 2023.05.07 |
---|---|
Unity - 게임 에셋 사이트들 추천! (0) | 2023.03.08 |
Unity - UI 자동화(Bind, Get) (0) | 2023.02.21 |
Unity - 플레이어가 이동할때 카메라가 플레이어 쳐다보게 하기 (0) | 2023.02.21 |
Unity - Raycast, 좌표계, LayerMask, Tag (0) | 2023.02.19 |
댓글