[Unity] Data binding으로 UI 관리하기

Running boy·2023년 8월 30일
0

유니티

목록 보기
6/9

이전 포스트에 이어서 이번엔 Data binding을 UI에 적용해보겠다.

Data binding은 말 그대로 '데이터를 묶는 것'이다.
서로 다른 두 데이터를 묶음으로써 한 쪽의 데이터 변화를 다른 데이터에도 반영하여 동기화를 이루는 것을 목표로 한다.

사실 이미 해당 기능을 구현해둔 에셋인 'Data Bind for Unity'를 스토어에서 판매중이다.
하지만 당장 내 프로젝트에서 저런 본격적인 기능이 필요한 것도 아니고 Data binding이 어떤 것인지 배울 겸 직접 구현해보려고 한다.
위 에셋은 나중에 협업이 필요한 상황에 사용해볼 예정이다.

대략적인 구조 설계

내가 생각한 구조를 간단하게 도식화하면 아래와 같다.

Data binding logic

  1. 게임 로직에서 발생하는 값의 변화를 데이터 컨테이너에 저장한다.
  2. UI View는 필요할 때 데이터 컨테이너에서 ID에 해당하는 값을 가져올 수 있다.
  3. ID에 해당하는 값이 변할 때 이벤트를 발생시켜 해당 ID를 참조하는 모든 UI View에 callback 메서드를 실행시킨다.

Binding data Class

데이터를 묶어서 관리할 데이터 클래스가 필요하다.

public class TextData
{
    public event Action<string> callback;

    private string _text;
    public string text
    {
        get
        {
            return _text;
        }
        set
        {
            _text = value;
            callback?.Invoke(value);
        }
    }
}

데이터 클래스는 텍스트 데이터와 콜백 이벤트로 구성된다.
콜백 이벤트는 텍스트 데이터의 값이 변할 경우에만 실행된다.

각 ID에 해당하는 데이터는 위 클래스에 담겨 컨테이너에 저장된다.

UI View 수정

전에 구현했던 UIView.cs에 다음의 코드를 추가한다.

static Dictionary<string, TextData> _dataBindingTextDictionary = null;

public static void SetValue(string dataID, string data)
{
    if (_dataBindingTextDictionary == null)
        _dataBindingTextDictionary = new Dictionary<string, TextData>();

    if (!_dataBindingTextDictionary.TryGetValue(dataID, out TextData textData))
    {
        textData = new TextData();
        _dataBindingTextDictionary.Add(dataID, textData);
    }

    textData.text = data;
}

public static TextData GetValue(string dataID)
{
    if (_dataBindingTextDictionary == null)
        _dataBindingTextDictionary = new Dictionary<string, TextData>();

    if (!_dataBindingTextDictionary.TryGetValue(dataID, out TextData textData))
    {
        textData = new TextData();
        _dataBindingTextDictionary.Add(dataID, textData);
    }

    return textData;
}

데이터 컨테이너를 DataID-TextData Pair의 dictionary로 구현했다.

외부에서 데이터 컨테이너로의 접근은 SetValue, GetValue 두 메서드를 통해서만 가능하고 각각 ID에 해당하는 값을 설정하거나 가져오는 메서드이다.

Data binding component 구현

using UnityEngine;
using UnityEngine.UI;

namespace Runningboy.UI
{
    public class DataBindingText : MonoBehaviour
    {
        [SerializeField]
        Text _textComponent;
        [SerializeField]
        string _dataID;

        private TextData _data;
        
        private void Reset()
        {
            _textComponent = GetComponent<Text>();
            _dataID = gameObject.name;
        }

        private void OnEnable()
        {
        	if (_textComponent == null)
            {
                _textComponent = gameObject.GetComponent<Text>();
                
                if (_textComponent == null)
                {
                    Debug.LogErrorFormat("{0} doesn't have a text component.", gameObject.name);
                }
            }
            if (string.IsNullOrEmpty(_dataID))
            {
                Debug.LogWarningFormat("Invalid text data ID. {0}", gameObject.name);
                _dataID = gameObject.name;
            }
            
            _data = UIView.GetValue(_dataID);
            _textComponent.text = _data.text;
            _data.callback += UpdateText;
        }

        private void OnDisable()
        {
            _data.callback -= UpdateText;
        }

        public void UpdateText(string text)
        {
            _textComponent.text = text;
        }
    }
}

Text 컴포넌트가 있는 오브젝트에 해당 컴포넌트를 같이 넣으면 된다.
컴포넌트에 설정한 ID에 해당하는 데이터를 컨테이너에서 받아와 동기화를 해줄 것이다.

개선할 부분

이 포스트에서는 Text 데이터에 대한 바인딩만 구현했다.
만약 다른 타입의 데이터(ex. TextMeshPro, Image)의 바인딩이 필요한 경우 추가적으로 컴포넌트 구현하고 컨테이너를 추가해야 한다.


참고 자료

Unity Korea - Dev Weeks: 작업 효율을 높이기 위한 유니티 UI 제작 프로그래밍 패턴들

profile
Runner's high를 목표로

0개의 댓글