앞선 게시글에서 말했듯이 UI를 툴 상에서 드래그&드롭으로 설정한다면 프로젝트 규모가 커질수록 단점이 많아지게 된다. 그렇기 때문에 UI를 코드 상에서 자동화 할 필요성이 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Define
{
public enum UIEvent
{
Click,
Drag,
}
public enum MouseEvent
{
Press,
Click,
}
public enum CameraMode
{
Quarterview,
}
}
UI의 클릭과 드래그를 구분하기 위해 Define.cs에 UIEvent라는 enum을 추가했다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class UI_Base : MonoBehaviour
{
Dictionary<Type, UnityEngine.Object[]> _objects = new Dictionary<Type, UnityEngine.Object[]>();
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] = Util.FindChild(gameObject, names[i], true);
else
objects[i] = Util.FindChild<T>(gameObject, names[i], true);
}
}
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 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); }
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;
}
}
}
Bind()를 통해 UI의 정보를 저장, Get()을 통해 정보를 가져온다.
AddUIEvent()를 통해 UI 이벤트의 구독을 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using UnityEngine.EventSystems;
public class UI_Button : UI_Base
{
enum Buttons
{
PointButton,
}
enum Texts
{
PointText,
ScoreText
}
enum GameObjects
{
TestObject,
}
enum Images
{
ItemIcon,
}
private void Start()
{
Bind<Button>(typeof(Buttons));
Bind<Text>(typeof(Texts));
Bind<GameObject>(typeof(GameObjects));
Bind<Image>(typeof(Images));
GetButton((int)Buttons.PointButton).gameObject.AddUIEvent(OnButtonClicked);
GameObject go = GetImage((int)Images.ItemIcon).gameObject;
AddUIEvent(go, (PointerEventData data) => { go.transform.position = data.position; }, Define.UIEvent.Drag);
}
int _score = 0;
public void OnButtonClicked(PointerEventData data)
{
_score++;
GetText((int)Texts.ScoreText).text = $"점수: {_score}";
}
}
확장메서드 기능을 통해 PointButton을 클릭할 시 OnButtonClicked() 함수가 실행된다.
또한 람다식을 통해 ItemIcon 이미지를 드래그할 수 있도록 하였다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Util
{
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 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;
}
public static T FindChild<T>(GameObject go, string name = null, bool recursive = false) where T : UnityEngine.Object
{
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)
{
T component = transform.GetComponent<T>();
if (component != null)
return component;
}
}
}
else
{
foreach (T component in go.GetComponentsInChildren<T>())
{
if (string.IsNullOrEmpty(name) || component.name == name)
return component;
}
}
return null;
}
}
FindChild() 함수를 통해 Bind()함수가 UI를 찾을 수 있도록 하였다.
GameObject를 찾는 경우에는 GameObject는 컴포넌트의 형식을 가질 수 없으므로 오버로딩을 통해 제네릭을 갖지 않는 함수 또한 작성하였다.
GetOrAddComponent() 함수는 이벤트 처리를 할 때 이벤트 처리를 할 UI가 EventHandler가 없을 시 추가하기 위해 작성하였다. 제네릭을 이용해 꼭 EventHandler가 아닌 다른 컴포넌트도 추가할 수 있도록 하였다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class UI_EventHandler : MonoBehaviour, IDragHandler, IPointerClickHandler
{
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);
}
}
UI 이벤트 처리를 위해 추가한 클래스이다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
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);
}
}
확장 메서드의 사용을 위해 추가한 클래스이다.