본문 바로가기
스파르타 내배캠

스파르타 내배캠 Unity 3기 32일차

by LemongO 2024. 2. 7.

생각났다! 뭐였더라?

 

 

오늘은 챌린지 세션에서 튜터님이 해주신 말씀이 너무 와 닿았다.

"기능만 구현하고 나중에 포트폴리오를 쓰려고 하면 의외로 쓸 게 없어요."

 

이젠 어떻게 구조를 잡을 것인지 생각하면서 코딩하는 연습을 해야 할 때가 온 것이겠지...

 

 


ui 자동화 첫 걸음

 

이제껏 모든 UI 관련을 [SerializeField] 나 public 으로 Inspector 상에서 드래그 드롭 방식으로 연결해 줬었다...

혼자 작업할 땐 혼자 하고 그냥 당장 내가 편하니까 이런 방식을 고수해왔었지만 이제 이 방식도 졸업해야겠지.

이젠 보기만해도 화가날 지경

 

어휴 어지럽다;;;

 

동적 생성과, UI 자동화를 위해 새로운 방법을 써보자.

 


UI_Base 클래스와 Bind / Get 그리고 Type

 

UI_Base 클래스는 모든 UI 가 상속받을 부모 클래스이다.

이 클래스에서 UI 생성 시, 모든 UI 컴포넌트를 Dictionary 로 바인드 하고 원할 때 쓸 수 있다.

 

그 기능을 하기 위해서 다음과 같은 기능들을 살펴보자

 

Bind

UI가 생성 될 때, 가지고 있길 원하는 컴포넌트들을 내부적으로 Dictionary에 저장하는 함수.

 

Dictionary<Type, UnityEngine.Object[]> _objects = new Dictionary<Type, UnityEngine.Object[]>();

void Bind<T>(Type type) where T : UnityEngine.Object // Enum의 Type을 넘겨주면 Enum안의 모든 Name과 같은 자식 오브젝트들을 검색, T 타입을 찾아 _objects에 Type키에 저장
{
    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++)
    {
        // gameObject의 자식을 전부 검사, 오브젝트 이름이 names[i]와 일치하면 T 타입을 반환.
        // 자세한 코드는 다음 TIL에서
        objects[i] = Util.FindChild<T>(gameObject, names[i], true);

        if (objects[i] == null)
            Debug.Log($"Failed to bind({names[i]})");
    }
}

 

  • 저장하고자 하는 타입 T를 넘긴다. 단, T는 UnityEngine.Object를 상속받는 타입만 해당된다.
  • Enum을 Type으로 변환 후 매개변수로 받아  Enum에 있는 요소들을 GetNames(type) 으로 string 배열에 담는다.
  • Dictionary에 저장 할 값 UnityEngine.Object[] 배열 objects를 names의 길이만큼 만든다.
  • Dictionary _objects 에 T 의 Type 을 "Key" 로, 위에서 만든 objects를 값으로 추가한다.
  • objects[i] 에 자기 자신의 자식들 중 names[i] 와 같은 이름을 가진 오브젝트에서 T 타입을 할당한다.

 

그렇다면 Bind 시 실제로 넘겨주는 Type 매개변수는 어떻게 넘길까?

 

    enum Buttons
    {
        Login_Btn,
        SignUp_Btn,
    }

 

UI_Base 를 상속받은 실제로 씬에서 쓰일 UI_Login 클래스 내부에 바인드 하고자 하는
클래스와 비슷하게 enum 을 만들고 (이름은 상관없지만 알기 쉽도록) 

실제 찾고자 하는 오브젝트와 동일한 이름으로 만들어 준다.

 

 

동일하게 하자

그 후 

public class UI_Login : UI_Base
{
    enum Buttons
    {
        Login_Btn,
        SignUp_Btn,
    }
    
    public override void Init()
    {
        Bind<Button>(typeof(Buttons));
    }
}

UI_Base 에서 생성 시 실행되는 Init 함수에 Bind 함수를 호출해 바인드를 원하는 타입의 클래스와 enum Type을 넘겨준다.

 

즉 실제로 쓰일 UI 오브젝트들은 모두 UI_Base 를 상속받은 오브젝트와 동일한 이름의 클래스를 가진다.

UI_Login 이 클래스 명이면 오브젝트 이름도 UI_Login 이다. (나중에 Instantiate 를 위해 맞출 필요가 있음.)

 

이렇게 Bind 함수를 호출하면 원하는 타입의 클래스가 _objects Dictionary 에 저장이 된다.

 

 

 

Get

바인드 된 타입중 원하는 값을 꺼내쓰는 함수

protected T Get<T>(int index) where T : UnityEngine.Object // Enum의 index를 넘겨주면 T Type키의 index번째 Object를 T 타입으로 캐스팅하여 반환
{
    UnityEngine.Object[] objs;

    if (_objects.TryGetValue(typeof(T), out objs) == false)
        return null;

    return objs[index] as T;
}

 

  • 쓰고자 하는 타입 T를 넘긴다.
  • 매개변수로 int index 를 넘긴다
  • 저장되어있는 typeof(T) 키에 UnityEngine.Object[] 값이 없으면 return null
  • 값이 있으면 objs[index] 값을 T 로 캐스팅 하여 반환한다.

이렇게 하면 원하는 타입에 바로 접근이 가능하다.

 

Get<Button>((int)Buttons.Login_Btn).onClick.AddListener(Login);

요런 식으로