[Unity] UI관리 기법

2020. 11. 2. 15:47[Unity] 게임 개발

반응형

Unity Client 개발자를 꿈 꾼다면 필수적으로 UI 관리 기법에 대해 잘 알고 있어야 한다.

 

소규모 팀 프로젝트를 진행할때는 UI 작업을 모두 직접 지정, OnClick 함수로 직접 지정해서 사용하여도 문제가 없고

효율성이 떨어지지 않을 것이다.

 

하지만 게임의 규모가 커지다보면 굉장히 많은 UI 작업을 모두 직접 작업하기 힘들고, 실수가 자주 일어나기 마련이다.

때문에 효율적으로 UI작업을 하는 방법에 대해 알아보았다.

 

우선 Unity 는 C#언어를 사용하니 C++과 달리 enum으로 string값을 가져오는 것이 가능하다.

 

Ex)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIManager : MonoBehaviour
{
    enum Buttons
    {
        PointButton,
    }

    enum Texts
    {
        PointText,
        ScoreText,
    }
    
    private void Start()
    {
        Bind<Button>(typeof(Buttons));
        Bind<Text>(typeof(Texts));
    }
    
    void Bind<T>(Type type)
    {
        String[] names = Enum.GetNames(type);
    }
}

 

이런식으로 사용하고 싶은 UI들을 enum을 통해 Button,Text 등등의 String 값을 저장하고 

Enum.GetNames() 함수를 사용해 그 저장된 값들의 String 값을 불러와서 사용하는 것이다. 

 

이제 그러한 각기 다른 타입 정보의 UI들을 String값만 받아와서 어떻게 사용할것인가에 대해 설명하자면

모든 Unity 객체는 상위 정보인 UnityEngine.Object 에서 내려온다.

그래서 우리는 Dictionary를 통해 <타입, 오브젝트> 로 두고 사용하면 되는 것이다.

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

 

이제 구조를 만들어 두었다면 Child를 찾는 클래스를 만들어야한다.

 

Ex)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Util
{
    public static T FindChind<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;
    }
}

 

구조는 간단하다. 들어온 String값이 최상위에 없다면 그대로 return할 것이고

 

있다면 그 오브젝트의 Child들을 검색할 것인지 안할 것인지,

안할것이면 반복문을 통해 원하는 값 도출.

할것이라면 go.GetComponentsInChildren<T>() 안에서 이름이 비어있거나 원하는 이름을 찾을때까지 반복문을 돌려주는 것이다.

 

이제 사용예시를 보자면

우선 Util클래서에서 사용한 자식들을 검색하는 FindChild 함수를 사용하여 바인딩하는 부분과, Get하여 실제로 사용하는 부분을 짜보았다.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

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));

        Get<Text>((int)Texts.ScoreText).text = "Bind Text"; //실험 부분
    }
    
    void Bind<T>(Type type) where T : UnityEngine.Object // UI 정보 바인딩
    {
        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.FindChind<T>(gameObject, names[i], true);
        }
    }

    T Get<T>(int idx) where T : UnityEngine.Object //UI 가져오는 부분
    {
        UnityEngine.Object[] objects = null;
        if (_objects.TryGetValue(typeof(T), out objects) == false)
            return null;

        return objects[idx] as T;
    }
}

이제 간단한 실행 예시로 Start문에서 ScoreText를 찾아와 그 이름을  Bind Text로 변경해 보았다.

그 결과 내가 직접 지정하지 않은 텍스트를 알아서 잘 찾아와 값을 잘 변경한 것을 확인할 수 있었다.

 

 

 

이렇게 UI관리 기법의 기본틀을 완성하였다.

반응형