이전 포스트에 이어서 이번엔 Data binding을 UI에 적용해보겠다.
Data binding은 말 그대로 '데이터를 묶는 것'이다.
서로 다른 두 데이터를 묶음으로써 한 쪽의 데이터 변화를 다른 데이터에도 반영하여 동기화를 이루는 것을 목표로 한다.
사실 이미 해당 기능을 구현해둔 에셋인 'Data Bind for Unity'를 스토어에서 판매중이다.
하지만 당장 내 프로젝트에서 저런 본격적인 기능이 필요한 것도 아니고 Data binding이 어떤 것인지 배울 겸 직접 구현해보려고 한다.
위 에셋은 나중에 협업이 필요한 상황에 사용해볼 예정이다.
내가 생각한 구조를 간단하게 도식화하면 아래와 같다.
데이터를 묶어서 관리할 데이터 클래스가 필요하다.
public class TextData
{
public event Action<string> callback;
private string _text;
public string text
{
get
{
return _text;
}
set
{
_text = value;
callback?.Invoke(value);
}
}
}
데이터 클래스는 텍스트 데이터와 콜백 이벤트로 구성된다.
콜백 이벤트는 텍스트 데이터의 값이 변할 경우에만 실행된다.
각 ID에 해당하는 데이터는 위 클래스에 담겨 컨테이너에 저장된다.
전에 구현했던 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에 해당하는 값을 설정하거나 가져오는 메서드이다.
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)의 바인딩이 필요한 경우 추가적으로 컴포넌트 구현하고 컨테이너를 추가해야 한다.
참고 자료