[[Unity UI Toolkit] 3. 직렬화된 오브젝트(SerializedObject) 데이터 바인딩

문연수·2022년 11월 12일
0

Unity UI Toolkit

목록 보기
4/4

 데이터 바인딩은 MonoBehaviourstring 프로퍼티와 같은 UI가 아닌 객체 속성에 대해 TextFieldvalue 프로퍼티와 같은 UI 객체 속성을 동기화한다. 바인딩은 수정 시에 프로퍼티와 visual control 사이의 연결을 참조한다.

 프로퍼티와 특정한 visual element 와의 값을 동기화는데 데이터 바인딩을 사용하게 되면, UI 값이 변화했을 때에 대한 event handler 로직을 작성할 필요가 없다.

Note: 직렬화된 오브젝트(SerializedObject) 데이터 바인딩은 런타임이 아닌 오직 에디터 상에서만 동작한다.

1. 직렬화 요구사항

 바인딩은 오직 직렬화된 프로퍼티에만 가능하다. 이는 visual element 를 직렬화 시스템(Serialization system) 과 호환되는 다음의 객체에 대해서만 바인딩할 수 있음을 의미한다:

  • 사용자가 정의한 ScriptableObject 클래스
  • 사용자가 정의한 MonoBehaviour 클래스
  • 토착(native) 유니티 컴포넌트 타입
  • 토착 유니티 에셋 타입
  • 원시 C# 자료형: int, bool, float, 기타 등등.
  • 토착 유니티 자료형: Vector3, Color, Object, 기타 등등.

2. 값 바인딩

visual elementvalue 프로퍼티는 INotifyValueChanged 인터페이스가 구현된 경우에만 바인딩을 할 수 있다. 예를 들어, TextField.valuestring 에 바인드하는 것은 가능하나, TextField.namestring 에 바인드하는 것을 불가능 하다.

visual elementBindableElement 를 기원하거나 혹은 IBindable 인터페이스를 구현했다고 한다면 오브젝트와 바인드할 수 있다.

3. 바인딩 생성하기

 바인딩을 생성하기 위해선, Bind() 혹은 BindProperty() 둘 중 하나를 호출해야 한다.

- Bind() 호출하기

 요소를 직렬화된 오브젝트와 바인드하기 위해 Bind() 함수를 호출할 수 있다. 요소를 바인드하기 전에, 반드시 바인딩 경로를 설정하고 직렬화된 오브젝트를 생성해야 한다.

 바인딩을 위한 SerializedProperty 에 대한 단순한 접근이 방법이 없는 경우라면 이 메서드를 써라.

Bind() 확장 메서드는 visual element 의 전체 계층을 특정 bindingPath 프로퍼티로 구성한다. 단일 요소 혹은 바인드를 원하는 계층의 부모에 Bind() 메서드를 호출할 수 있다. 예를 들어, 에디터 윈도우의 rootVisualElement 에 대해 Bind() 메서드를 호출할 수 있다. 이는 모든 자식 요소들을 특정 bindingPath 프로퍼티와 바인드한다.

Editor.CreateInspectorGUI() 혹은 PropertyDrawer.CreatePropertyGUI() 오버라이드로부터 Bind() 를 호출해선 안된다. 이러한 오버라이드들은 메서드가 반환되면서 visual element 에 대해 자동적으로 호출되어진다.

- Unbind() 호출하기

Unbind() 메서드는 요소에 대한, 그리고 직간접적인 자식 요소에 대해서도 값의 추적을 중단한다. 일반적으로, 사용자가 인스펙터 혹은 에디터 윈도우를 닫으면 추적은 중단되기에 Unbind() 함수를 호출할 필요는 없다. 생애 주기 내에 서로 다른 대상과 바인드해야 하는 경우에 한하여 Unbind() 를 호출하라.

 C# 의 생성자의 호출을 통해 InspectorElement 구성했다면, 바인딩은 생성자 호출 시점에 이뤄져야 한다. 만일 InspectorElement 를 생성된 이후에 다시 바인드하길 원한다면 반드시 Unbind() 를 먼저 호출한 뒤 Bind() 를 명시적으로 호출하거나 부모의 바인딩 생성으로부터 바인드가 수행되도록 해야 한다.

- 바인딩 경로(Binding path) 설정하기

 만일 바인딩 생성을 위해 Bind() 를 호출했다면, 반드시 visual element 의 바인딩 경로를 바인드하길 원하는 오브젝트의 프로퍼티 명으로 지정해야 해야 한다. 예를 들어:

  • 만일 다음의 컴포넌트를 스크립트에서 가질 때:

    using UnityEngine;
    
    public class MyComp : MonoBehaviour
    {
        [SerializeField]
        int m_Count;
    }

    visual elementm_Count 에 바인드하려면, m_Count 의 바인딩 경로를 지정해야 한다.

  • 만일 visual elementGameObject 의 이름 프로퍼티(m_Name)와 바인드하길 원한다면, 바인딩 경로를 m_Name 으로 설정해야 한다.

바인딩 경로를 UI Builder, UXML 혹은 C# 스크립트를 통해 설정할 수 있다:

  • UI Builder 에선, Inspectorvisual element 에 대한 바인딩 경로를 Binding Path 필드에 입력할 수 있다.
  • UXML 에선, visual element 에 대한 binding-path 속성을 설정할 수 있다.
  • C# 에선, IBindable 인터페이스로부터 bindingPath 를 설정할 수 있다.

- BindProperty() 호출하기

SerializedProperty 와 요소를 직접 바인드하기 위해 BindProperty() 를 호출할 수 있다.

이미 SerializedProperty 를 가지는 객체에, 그리고 특히 UI 를 동적으로 빌드하기 위해 SerializedObject 프로퍼티를 횡단하는 경우 이 메서드를 사용하라.

4. 중첩된 프로퍼티에 대해 요소 바인딩하기

visual element 를 소스 오브젝트 내의 중첩된 프로퍼티에 대해 바인드 할 수 있다. 이를 위해선, 원소의 바인딩 경로와 초대의 바인딩 경로를 합쳐야 한다. 다음의 요소들에 대해 이 메서드를 사용할 수 있다:

  • BindableElement
  • TemplateContainer (UXML 의 <Instance> 태그와 대응하는)
  • GroupBox

5. 값이 변화했을 때 콜백 받기

 바인딩된 직렬화 속성이 변했을 때에 대한 콜백을 받기 위한 바인드를 생성할 수 있다. 이를 위해선, TrackPropertyValue() 확장 메서드를 이용할 수 있고 이는 어떠한 VisualElement 에 대해서도 이용이 가능하다. 이는 제공된 SerializedProperty 가 변경되었을 때 호출되는 콜백을 등록한다.

 또한 바인딩된 직렬화된 객체의 어떤 속성이 변경되었을 때, 콜백을 전달받기 위한 바인딩을 생성할 수 있다. 이를 위해, TrackSerializedObjectValue() 확장 메서드를 이용할 수 있고 이는 제공되어진 SerializedProperty 가 변했을 때 실행하는 콜백을 등록한다.

6. 커스텀 요소 바인드

value binding 시스템을 통해 커스텀 요소를 만들 수 있고 이들을 직렬화된 프로퍼티와 바인드 할 수 있다.

바인드 가능한 커스텀 요소를 만들기 위해서는:

  1. 커스텀 요소를 선언한다.
  2. BindableElement 로 부터 요소를 상속 받거나 IBinding 인터페이스를 구현한다.
  3. INotifyValueChanged 인터페이스를 구현한다.
  4. INotifyValueChanged 에서 대한 SetValueWithoutNotify() 메서드를 구현한다.
  5. INotifyValueChanged 인터페이스에 대한 value 프로퍼티 접근자를 구현한다.

 커스텀 인스펙터를 작성할 때 암시적으로 직렬화된 객체를 위 에디터 윈도우 예시와 같이 visual tree 에 결합할 필요가 없다. 이러한 과정은 CreateInspectorGUI 메서드가 끝난 이후에 묵시적으로 이뤄진다. 이러한 자동화된 바인딩 과정은 오직 그 시점에서만 수행되어 진다. 만일 CreateInspectorGUI 메서드 콜백 밖에서 필드를 추가한다면, 이를 직접 바인드할 필요가 있다. 그렇지 않다면, 이는 렌더링에 실패하거나 정의되지 않은 visual 결과를 야기할 것이다.

7. 제일 좋은 방법

 생성한 UI 타입에 따라 바인딩은 다양한 시점에 이뤄지며 이는 바인드 타임이라 불린다. 이하의 표는 컨트롤의 바인드 타임을 서술한다:

ConditionAutomatic bind time (assuming binding path was set)
An InspectorElement constructed in C#During the constructor call
A child element that is under the return value of CreateInspectorGUI() or CreatePropertyGUI() when those methods returnAfter CreateInspectorGUI() or CreatePropertyGUI() returns
A child element that is under an element when Bind() or BindProperty() is called on the parent elemenetDuring the Bind() or BindProperty() call
OtherNo automatic binding; you must bind the element or one of its parents manually

바인드 타임에 따라 바인딩을 생성할 때 다음의 방식이 제일 좋다:

  • 만일 커스텀 Editor 혹은 커스텀 PropertyDrawer 를 생성했다면, 요소의 바인딩 경로를 설정하는 대신 visual tree 내의 어떤 visual element 에 대해 CreateInspectorGUI() 혹은 CreatePropertyGUI() 몸체의 끝에서 Bind() 혹은 BindProperty() 를 호출하라.
  • 만일 다른 타입의 UI 를 생성한 경우, visual tree 에 어떤 요소가 추가되었는지 그 때와 관계없이 Bind() 혹은 BindProperty() 를 호출하라. 만일 Bind() 혹은 BindProperty() 그리고 같은 시점에 다수의 컨트롤에 대해 바인드를 호출했다면, 각 컨트롤의 바인드 패스를 설정한 뒤에 가장 낮은 레벨의 부모 요소부터 시작해서 모든 컨트롤에 대해 Bind() 를 호출하라. Bind() 는 호출된 요소가 바인딩 패스를 가졌다면 해당 요소에 대해 바인드하고 재귀적으로 자식 역시 바인딩 패스를 가진다면 그 자식 요소들 또한 바인드한다. 부정적인 성능 영향 예방하기 위해선, Bind() 메서드를 통해 한번 이상 visual element 를 바인드해선 안된다.

출처

[사이트] https://docs.unity3d.com/2021.3/Documentation/Manual/UIE-Binding.html

profile
2000.11.30

0개의 댓글