public class UI_Button : MonoBehaviour
{
Dictionary<Type, UnityEngine.Object[]> _objects = new Dictionary<Type, UnityEngine.Object[]>();
enum Buttons
{
PointButton
}
enum Texts
{
PointText,
ScoreText
}
private void Start()
{
Bind<Button>(typeof(Buttons));
Bind<Text>(typeof(Texts));
}
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++)
{
objects[i] = Util.FindChild<T>(gameObject, names[i], true);
if (objects[i] == null)
Debug.Log($"Failed to bind({names[i]})");
}
}
}
UI Canvas에 부착한 스크립트에서 위와 같이 오브젝트를 찾기 위한 코드를 작성한다
해당 오브젝트의 자식 요소에서 특정한 이름을 가진 오브젝트들을 검색할 건데, 이를 Assets > Scripts > Utils 폴더 내에 Util이라는 스크립트를 만들어 기능성 함수들을 관리하는 클래스를 생성한다
public class Util
{
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++)
{
Transfrom transform = go.transform.GetChild(i);
if (string.IsNullOrEmpty(name) || transform.name == name)
{
T component = transfrom.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 함수를 만들어준다
이어서 찾은 오브젝트들을 받아와서 사용할 수 있게 Get 함수를 만들어준다(UI_Button.cs)
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;
}
Get<Text>((int)Texts.ScoreText).text = "변경할 텍스트"
사용할 때에는 위와 같이 T 타입 명시, enum을 사용한 텍스트명을 넘겨주면 된다
위에서는 component를 기준으로 찾았기 때문에, 그냥 gameobject 자체를 찾고 싶은 경우 에러가 발생하게 된다
public class UI_Button : MonoBehaviour
{
Dictionary<Type, UnityEngine.Object[] _objects = new Dictionary<Type, UnityEngine.Object[]();
enum Buttons
{
PointButton
}
enum Texts
{
PointText,
ScoreText
}
enum GameObjects
{
TestObject
}
private void Start()
{
Bind<Button>(typeof(Buttons));
Bind<Text>(typeof(Texts));
Bind<GameObjects>(typeof(GameObjects));
}
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($"Failed 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;
}
}
따라서 위와 같이 gameobject를 받을 수 있도록 버전을 따로 만들어주고, Util 스크립트 내 함수에서도 아래와 같이 다른 버전의 함수를 추가해준다
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;
}
모든 게임 오브젝트가 가지고 있는 컴포넌트인 transform을 가지고 findchild 함수를 호출시켜서 return 시키면 된다
마지막으로 text, button, image 같은 경우 좀 더 편리하게 꺼내 쓸 수 있게 몇 가지 함수를 추가시켜준다(UI_Button.cs)
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 함수 대신 GetText 등의 함수를 사용해서 바인딩 된 것들을 뽑아올 수 있다
Get<Text>((int)Texts.ScoreText).text = "Bind Test";
GetText((int)Texts.ScoreText).text = "Bind Test";
위와 같이 작성한 코드들을 모든 UI에 복붙시켜서 사용하는 대신 Assets > Scripts > UI > UI_Base.cs 스크립트를 하나 만들어 아래와 같이 작성하여 사용한다
public class UI_Base : MonoBehaviour
{
protected 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);
if (objects[i] == null)
Debug.Log($"Failed to bind({names[i]})");
}
}
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); }
}
이렇게 작성한 뒤에, UI_Button.cs 파일에서는 MonoBehaviour 클래스 대신 UI_Base를 상속받아서 사용하면 된다
* UI_Base가 MonoBehaviour을 상속받기 때문에 UI_Button에도 MonoBehaviour이 들어간다
* UI_Base의 함수와 변수는 보호 수준을 protected로 설정하여 자식 클래스가 사용할 수 있게 해주어야 한다
public class UI_Button : UI_Base
{
enum Buttons
{
PointButton
}
enum Texts
{
PointText,
ScoreText
}
enum GameObjects
{
TestObject
}
private void Start()
{
Bind<Button>(typeof(Buttons));
Bind<Text>(typeof(Texts));
Bind<GameObjects>(typeof(GameObjects));
}
}
'강의, 책 > [Unity] C#과 유니티로 만드는 MMORPG 게임 개발 시리즈' 카테고리의 다른 글
Section 7. UI - UI Manager (0) | 2024.01.30 |
---|---|
Section 7. UI - UI 자동화(이벤트) (0) | 2024.01.26 |
Section 6. Animation - State 패턴 (0) | 2024.01.24 |
Section 6. Animation - Animation Blending (0) | 2024.01.24 |
Section 5. Camera (0) | 2024.01.24 |