바인딩 오브젝트를 생성할 때는 데이터 소스를 정의해야 합니다. 데이터 소스는 바인딩하려는 프로퍼티가 포함된 오브젝트입니다. 모든 C# 오브젝트를 런타임 바인딩 데이터 소스로 사용할 수 있습니다.
바인딩 시스템이 데이터 소스에 액세스하도록 만들려면 바인딩 오브젝트의 데이터 소스 오브젝트에 대한 dataSource 프로퍼티를 정의해야 합니다. 예를 들어 다음과 같은 데이터 소스 오브젝트와__ UI__(사용자 인터페이스) 사용자가 애플리케이션과 상호 작용하도록 해 줍니다. Unity는 현재 3개의 UI 시스템을 지원합니다. 자세한 정보
See in Glossary 요소가 있는 경우:
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Properties;
public class DataSource
{
public Vector3 vector3 { get; set; }
}
var element = new VisualElement();
그런 다음 다음과 같이 데이터 소스 오브젝트에 대한 element.dataSource 프로퍼티를 정의할 수 있습니다.
element.dataSource = new DataSource();
이렇게 하면 요소에 적용된 바인딩이 DataSource 오브젝트에 액세스할 수 있습니다.
요소에 적용된 바인딩이 DataSource 오브젝트의 vector3 필드에 액세스할 수 있도록 하려면 다음을 추가합니다.
element.dataSourcePath = PropertyPath.FromName(nameof(DataSource.vector3));
자식 요소에 적용된 바인딩이 DataSource 오브젝트의 vector3 필드에 액세스할 수 있도록 하려면 다음을 추가합니다.
var child = new VisualElement();
child.dataSourcePath = PropertyPath.FromName(nameof(DataSource.vector3));
element.Add(child)
UI 툴킷은 Unity.Properties 모듈을 사용하여 두 오브젝트 간에 데이터를 바인딩하기 위한 프로퍼티 백을 만듭니다. 사용 가능한 C# 유형 정보를 기반으로 프로퍼티 백을 생성합니다. 하지만 특정 빌트인 Unity 유형의 경우 생성된 프로퍼티 백에 예상되는 프로퍼티가 포함되지 않을 수 있습니다. 이는 이러한 유형에 필요한 속성이 없는 경우에 발생할 수 있습니다. 예를 들어 Rect 유형에 [SerializeField]로 기여하지 않은 공용 프로퍼티와 비공개 필드가 있거나, 런타임 시 확인할 수 없는 네이티브 측면에서 필드를 정의할 수 있습니다.
참고: 값 유형을 데이터 소스로 사용하는 경우 오브젝트 프로퍼티로 정의되는 VisualElement.dataSource로 인해 박싱 비용이 발생합니다. 즉, 값 유형을 dataSource 프로퍼티에 할당하기 전에 해당 값 유형을 박싱해야 합니다. 박싱 작업으로 인해 메모리 할당과 복사에 오버헤드가 발생하여 성능 소모가 증가합니다. 작은 데이터 세트나 가끔씩 사용되는 경우 성능에 미치는 영향은 중요하지 않을 수 있습니다. 하지만 성능이 중요한 시나리오나 대량의 데이터를 처리하는 경우 박싱 비용이 문제가 될 수 있습니다.
런타임 바인딩과 저작 또는 직렬화 목적으로 데이터 소스를 정의하려면 아래와 같이 공통 패턴을 사용하십시오.
using UnityEngine;
using Unity.Properties;
public class MyBehaviour : MonoBehaviour
{
// Serializations go through the field.
[SerializeField, DontCreateProperty]
private int m_Value;
// Bindings go through the property rather than the field.
// This allows you to do validation, notify changes, and more.
[CreateProperty]
public int value
{
get => m_Value;
set => m_Value = value;
}
// This is a similar example, but for an auto-property.
[field: SerializeField, DontCreateProperty]
[CreateProperty]
public float floatValue { get; set; }
}
참고: 이러한 바인딩 가능한 프로퍼티는 본질적으로 다형성을 갖습니다.
성능을 향상시키기 위해 버전 지정 및 변경 추적을 바인딩 데이터 소스에 통합할 수 있습니다. 기본적으로 바인딩 시스템은 데이터 소스를 지속적으로 폴링하고 마지막 업데이트 이후 실제로 변경된 사항이 있는지 확인하지 않은 상태에서 모든 수정 사항에 대해 UI를 업데이트합니다. 이 접근 방식은 간단한 프로젝트에서는 편리하지만, 수많은 바인딩을 처리할 때는 효율적으로 확장되지 않습니다.
소스에 대한 버전 지정 및 변경 추적은 의도적으로 활성화해야 하는 선택적 기능입니다. 기본적으로 활성 바인딩 오브젝트는 프레임마다 업데이트되며, 리소스를 많이 소모하는 프로세스일 수 있습니다. 처리 오버헤드를 최소화하기 위해 두 가지 인터페이스를 구현하여 소스와 관련된 바인딩을 업데이트할 시기를 바인딩 시스템에 알릴 수 있습니다.
IDataSourceViewHashProvider 인터페이스는 소스에 연결된 모든 바인딩을 업데이트할 시기를 나타내는 뷰 해시 코드를 제공합니다.INotifyBindablePropertyChanged 인터페이스를 사용하면 프로퍼티별 변경 알림을 통해 수정된 프로퍼티와 관련된 개별 바인딩에 대한 업데이트만 트리거할 수 있습니다.보다 세밀한 제어를 위해 이러한 인터페이스를 별도로 또는 함께 구현할 수 있습니다.
참고: 현재 두 인터페이스 중 하나를 구현하는 유형은 어셈블리가 [assembly: Unity.Properties.GeneratePropertyBagsForAssembly]로 태그될 때 자동으로 코드 생성에 옵트인합니다. 하지만 이러한 동작은 변경될 수 있습니다.
IDataSourceViewHashProvider 구현특정 소스에 대한 뷰 해시 코드를 제공하려면 IDataSourceViewHashProvider 인터페이스를 구현합니다. 이 인터페이스를 사용하면 소스가 마지막 업데이트 이후 변경되지 않은 경우 바인딩 시스템이 특정 바인딩 오브젝트 업데이트를 건너뛸 수 있습니다.
다음 예시에서는 변경 사항을 즉시 보고하는 데이터 소스를 생성합니다.
using UnityEngine.UIElements;
public class DataSource : IDataSourceViewHashProvider
{
public int intValue;
public float floatValue;
// Determines if the data source has changed. If the hash code is different, then the data source
// has changed and the bindings are updated.
public long GetViewHashCode()
{
return HashCode.Combine(intValue, floatValue);
}
}
IDataSourceViewHashProvider 인터페이스는 변경 사항도 버퍼링합니다. 이 버퍼링 기능은 데이터가 자주 변경될 때 특히 유용하지만, UI가 모든 변경 사항을 즉시 반영할 필요는 없습니다.
변경 사항을 버퍼링하려면 IDataSourceViewHashProvider 인터페이스를 구현하고 데이터 소스가 변경되었음을 바인딩 시스템에 알리려는 경우 CommitChanges 메서드를 호출합니다.
기본적으로 데이터 소스 버전이 변경되지 않은 경우 바인딩 시스템은 바인딩 오브젝트를 업데이트하지 않습니다. 그러나 버전이 변경되지 않았더라도 해당 MarkDirty 메서드를 호출하거나 updateTrigger를 BindingUpdateTrigger.EveryFrame으로 설정하면 바인딩 오브젝트를 여전히 업데이트될 수 있습니다. IDataSourceViewHashProvider를 사용하여 변경 사항을 버퍼링할 때 목록에서 항목을 추가 또는 제거하거나 하위 필드 또는 하위 프로퍼티의 유형을 변경하는 등 소스의 구조적 변경 사항을 지양하십시오.
다음 예시에서는 변경 사항을 버퍼링하는 데이터 소스를 생성합니다.
using UnityEngine.UIElements;
public class DataSource : IDataSourceViewHashProvider
{
private long m_Version;
public int intValue;
public void CommitChanges()
{
++m_Version;
}
// Required by IDataSourceViewHashProvider
public long GetViewHashCode()
{
return m_Version;
}
}
INotifyBindablePropertyChanged 구현특정 프로퍼티 변경 사항을 바인딩 시스템에 알리려면 INotifyBindablePropertyChanged 인터페이스를 구현합니다. 이 인터페이스를 구현하면 프로퍼티 경로에 따른 변경 사항이 감지될 경우 바인딩 시스템이 관련 바인딩만 업데이트합니다. 예를 들어, MyAwesomeObject 프로퍼티에 대한 변경 사항 관련 신호가 발생하면 바인딩 시스템은 접두사가 MyAwesomeObject인 데이터 소스 경로와 관련된 모든 바인딩을 업데이트합니다. 소스에 연결된 다른 바인딩 오브젝트는 영향을 받지 않습니다.
이 접근 방식은 바인딩 시스템이 최소한의 작업을 수행하므로 UI를 매우 효율적으로 업데이트할 수 있습니다.
다음 예시에서는 프로퍼티별로 변경 사항을 알리는 데이터 소스를 생성합니다.
using System.Runtime.CompilerServices;
using Unity.Properties;
using UnityEngine.UIElements;
public class DataSource : INotifyBindablePropertyChanged
{
private int m_Value;
// Required by INotifyBindablePropertyChanged
public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
[CreateProperty]
public int value
{
get => m_Value;
set
{
if (m_Value == value)
return;
m_Value = value;
Notify();
}
}
void Notify([CallerMemberName] string property = "")
{
propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(property));
}
}
참고: INotifyBindablePropertyChanged 인터페이스를 구현하면 바인딩 시스템이 변경 사항을 알릴 때 확인을 수행하지 않습니다. 변경 사항을 보고하지 못하면 바인딩 시스템이 해당 프로퍼티와 관련된 바인딩을 업데이트하지 않습니다. 따라서 필요한 경우에만 변경 사항을 보고해야 합니다.
IDataSourceViewHashProvider 및 INotifyBindablePropertyChanged 구현최적의 바인딩 성능을 얻으려면 IDataSourceViewHashProvider 및 INotifyBindablePropertyChanged 인터페이스를 모두 구현하십시오. 바인딩 시스템은 뷰의 해시 코드가 변경될 때까지 변경된 프로퍼티를 추적합니다. 이때는 변경된 프로퍼티와 연결된 영향을 받는 바인딩만 효율적으로 업데이트합니다.
추가로 보일러플레이트 코드가 필요하지만 유연성과 성능상의 이점을 극대화할 수 있습니다.
다음 예시는 두 인터페이스를 모두 구현하는 데이터 소스를 생성합니다. 데이터 소스는 변경 사항이 발생하면 바인딩 시스템에 알림을 보냅니다. 그러나 바인딩을 즉시 업데이트하는 대신, Publish() 메서드가 호출될 때까지 업데이트가 보류됩니다. 이 접근 방식은 프레임마다 UI를 업데이트하면 성능 소모가 발생하는 매우 휘발성이 높은 데이터를 처리할 때 특히 유용합니다.
using System;
using System.Runtime.CompilerServices;
using Unity.Properties;
using UnityEngine.UIElements;
public class DataSource : IDataSourceViewHashProvider, INotifyBindablePropertyChanged
{
private long m_ViewVersion;
private int m_Value;
private int m_OtherValue;
public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
[CreateProperty]
public int value
{
get => m_Value;
set
{
if (m_Value == value)
return;
m_Value = value;
Notify();
}
}
[CreateProperty]
public int otherValue
{
get => m_OtherValue;
set
{
if (m_OtherValue == value)
return;
m_OtherValue = value;
Notify();
}
}
public void Publish()
{
++m_ViewVersion;
}
public long GetViewHashCode()
{
return m_ViewVersion;
}
void Notify([CallerMemberName] string property = "")
{
propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(property));
}
}
다음 팁과 베스트 프랙티스를 따라 성능을 최적화하십시오.
바인딩 가능한 프로퍼티에 C# 프로퍼티 사용: 바인딩 가능한 프로퍼티를 정의할 때 필드 대신 C# 프로퍼티를 사용합니다. 이를 통해 확인, 알림 또는 모든 커스텀 동작을 유연하게 통합할 수 있어 더욱 강력하고 유지 관리가 쉬운 코드를 만들 수 있습니다.
C# 프로퍼티에서 광범위한 계산 지양: 프로퍼티에 상당한 처리가 필요한 경우, 필요할 때만 계산을 수행하고 후속 바인딩에는 캐시된 값을 사용합니다.
불필요한 알림 방지: 값에 실제 변경 사항이 없을 때 변경 사항을 알리는 것에 주의하십시오. 값이 동일하게 유지되면 알림을 전송할 필요가 없습니다.
버전 지정 및 변경 추적 구현: 데이터 소스에서 버전 지정을 사용합니다. 최적의 성능을 얻으려면 버전 지정과 변경 추적을 모두 사용하십시오.
데이터와 UI 간의 버퍼로 데이터 소스 사용: 가능한 경우 데이터를 직접 사용하는 대신 데이터와 UI 간의 중개 역할로 데이터 소스를 구현합니다. 이 접근 방식은 다음과 같은 몇 가지 이점을 제공합니다.
데이터 플로를 더 잘 제어하고 UI에서 발생하는 변경 사항을 쉽게 추적할 수 있습니다. 이를 통해 데이터가 언제, 어떻게 업데이트되는지 관리할 수 있습니다.
모든 UI 데이터를 한 위치에 중앙 집중식으로 구성하여 데이터 액세스를 단순화하고 애플리케이션 전체의 복잡도를 줄일 수 있습니다.
원본 데이터의 깔끔함과 효율성을 유지하여 유형에 대한 추가 계측이 필요 없고 데이터 무결성이 보장됩니다.
다음 섹션에서는 런타임 바인딩 데이터 소스의 알려진 제한 사항을 간략하게 설명합니다.
정적 유형을 데이터 소스로 사용할 수 없습니다. 시스템이 작동하려면 유형의 인스턴스를 생성해야 합니다.
유형에 대해 생성된 프로퍼티 백은 필드와 프로퍼티만 고려합니다. 따라서 메서드나 빌트인 이벤트에 바인딩할 수 없습니다.
그러나 Action 또는 Func 델리게이트 유형과 같은 델리게이트에는 바인딩할 수 있습니다. 델리게이트 필드 또는 프로퍼티에 바인딩하려면 += 또는 -= 대신 = 연산자를 사용합니다. 델리게이트를 할당하는 대신 추가하거나 제거해야 하는 경우 커스텀 바인딩 유형을 구현해야 할 수 있습니다.
정적 유형 섹션에서 언급한 대로 데이터 소스의 오브젝트 인스턴스를 생성해야 합니다. 바인딩 시스템은 인터페이스와 함께 작동하지만, [CreateProperty]로 태그가 지정된 프로퍼티를 사용하여 인터페이스를 구현하는 유형에는 바인딩 가능한 프로퍼티가 자동으로 생성되지 않습니다. 각 유형의 필드와 프로퍼티에 각각 태그를 지정해야 바인딩이 가능합니다. 이 제한은 향후 릴리스에서 해결될 예정입니다.
C#의 프로퍼티 백 생성 프로세스는 주로 사용자 정의 유형과 함께 작동하도록 설계되었습니다. 따라서 현재 Unity의 빌트인 컴포넌트 및 오브젝트에 대한 지원이 제한적입니다. 이는 네이티브 코드에서 정의되는 빌트인 유형의 필드, 엔진의 명시적 직렬화 처리, 또는 [SerializeField] 속성의 부재 등 다양한 요인으로 인해 발생합니다. 하지만 사용자 정의 컴포넌트와 스크립터블 오브젝트의 필드와 프로퍼티는 예상대로 작동합니다.
이 제한은 향후 릴리스에서 해결될 예정입니다. 그동안 두 가지 해결 방법을 사용할 수 있습니다.
private 프로퍼티를 추가하여 바인딩 시스템에 노출하십시오.Transform)의 필드나 프로퍼티를 사용하려면 필수 프로퍼티를 노출하는 래퍼 유형을 만드십시오.