정리/UNITY

효율적 UI_Component 바인딩

원클릭쓰리버그 2022. 3. 8. 17:34
728x90

UI

 

필요성

 

흔히 우리는 Inspector에 오브젝트를 끌어와 사용한다.

하지만 프로젝트가 점점 커지고,  UI 요소가 많아지면 Inspector 복잡해지며, 알아보기 힘든 경우가 많다. 

또한, Inspector의 정보값이 달라진다면, 값이 누락되어 예기치 않은 터짐이 발생한다.

 

이러한 문제점을 해결하고, 접근성을 높이기 위해 UI의 Utility를 높일 필요성을 느꼈다.

 

 

UI_Base

 

UI_Base는 UI 컨트롤에 베이스역할을 해준다. 

 

Dictionary를 통해 Enum 타입에 따라 유니티 오브젝트를 저장하고 필요할 때, 연결시켜주는 기능을 수행한다.

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

public abstract class UI_Base : MonoBehaviour
{
    public GAMESTATE m_GameState = GAMESTATE.None;

    public abstract void StartUI();

    public abstract void EndUI();

    public virtual void OnDestroy()
    {
        EndUI();
    }


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


	protected void Bind<T>(Type type) where T : UnityEngine.Object
	{
		if (_objects.ContainsKey(typeof(T)))
			return;

		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 GameObject GetObject(int idx) { return Get<GameObject>(idx); }
	protected T GetObjScript<T>(int idx) { return Get<GameObject>(idx).GetComponent<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); }
}

 

 

Util

 

 

FindChild<T>의 경우 2가지로 나뉜다. 

 

recursive = true 

-> 찾아야하는 오브젝트가 깊은 Child일때

    상위 Parent에서 Child의 깊은 Child까지 하나하나 체크한다.

 

recursive = false

-> 찾아야하는 오브젝트가 상위 Parent의 직계 Child일때,

    직계 Child만 체크한다.

 

여기서 어려웠던 포인트에 대한 개념 기억하기 

링크 : https://docs.unity3d.com/kr/530/ScriptReference/Component.GetComponentsInChildren.html

 

Unity - 스크립팅 API: Component.GetComponentsInChildren

Success! Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable. 닫기

docs.unity3d.com

 

component.getcomponentinchildren<T>() = 활성화된 자식의 컴포넌트

component.getcomponentinchildren<T>(true) = 비활성화까지 포함한 자식의 컴포넌트

using UnityEngine;

public class 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;
    }

    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>(true))
            {
                if (string.IsNullOrEmpty(name) || component.name == name)
                    return component;
            }
        }

        return null;
    }

}

 

 

사용 예)

 

MainPage

 

 

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

public class MainPage : UI_Base
{
    enum Buttons
    {
        State_Btn,
        Magic_Btn
    }

    //enum Texts
    //{
    //    PointText,
    //    ScoreText,
    //}

    //enum GameObjects
    //{
    //    TestObject,
    //}

    //enum Images
    //{
    //    ItemIcon,
    //}


    [Header("   ===== Setting ===== ")]
    GameObject panel;
    Transform[] UI;
    float width, height;
    UIManager ui_mgr;
    GameController GameMgr;

    public override void StartUI()
    {
        ui_mgr = UIManager.Instance;
        GameMgr = GameController.Instance;

        panel = transform.GetChild(0).GetChild(0).gameObject;
        width = panel.GetComponent<RectTransform>().rect.width;
        height = panel.GetComponent<RectTransform>().rect.height;

        ui_mgr.Init(width, height);

        Init_UI();

        Set_Btn();
    }

    void Init_UI()
    {
        if (transform.localScale == Vector3.zero)
            transform.localScale = new Vector3(1, 1, 1);

        UI = new Transform[transform.GetChild(0).GetChild(0).childCount];
        for (int i = 0; i < UI.Length; i++)
        {
            UI[i] = transform.GetChild(0).GetChild(0).GetChild(i);
            UI[i].localScale = new Vector2(ui_mgr.Ratio_W, ui_mgr.Ratio_H);
        }

        Bind<Button>(typeof(Buttons));

    }
    void Set_Btn()
    {
        GetButton((int)Buttons.State_Btn).onClick.AddListener(ClickBtn_Game);
        GetButton((int)Buttons.Magic_Btn).onClick.AddListener(ClickBtn_Magic);
    }
    public override void EndUI()
    {
    }


    #region Btn
    void ClickBtn_Game()
    {
        GameMgr.Update_GameState(GAMESTATE.StatePage);
    }

    void ClickBtn_Magic()
    {
        GameMgr.Update_GameState(GAMESTATE.LibraryPage);
    }
    #endregion

}

 

Enum 값을 나누고, 해당되는 오브젝트의 이름을 Enum 값으로 정의한다. 이를 UI_Base를 이용하여 바인딩을 실시하고, 정상적으로 값이 받아졌다면, 꺼내어 쓴다.

'정리 > UNITY' 카테고리의 다른 글

Menu_UI 기능 구현  (0) 2022.04.25
IL2CPP  (0) 2022.03.19
Unity Shader (셰이더)  (0) 2022.03.08
Line Rederer (라인 렌더러)  (0) 2022.02.09
효과적인 에셋 관리를 위한 [Addressable Assets System]  (0) 2022.01.21