UDN
Search public documentation:

UIDataStoreKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 유저 인터페이스와 HUD > UI 데이터 저장소 시스템

UI 데이터 저장소 시스템


개요


데이터 저장소 시스템은 게임 내 다양한 시스템 간에 데이터를 조작 및 전달하기 위한 강력하고 유연한 메커니즘입니다. 이 문서에서는 데이터 저장소 시스템의 주요 개념과 아키텍처를 파악하고 데이터 저장소 시스템의 구성 요소가 어떻게 함께 모든 유형의 데이터 작업을 위한 통합 인터페이스를 제공하는지 설명합니다.

이 문서(UDN 위키의 모든 문서와 마찬가지로)는 누군가가 수정할 수 있습니다. 잘못되었거나 오래된 정보가 있거나 명확한 설명이 필요한 경우 독자가 필요한 변경을 수행하여 이 문서에서 제시한 정보의 가독성, 유용성 또는 정확성을 높일 수 있습니다.

용어


data store tag
등록된 데이터 저장소 인스턴스를 고유하게 식별하는 이름입니다. 보통 데이터 저장소의 값은 'Tag' 등록 정보이지만 UUIDataStore::GetDataStoreID() 메소드가 덮어쓸 수 있습니다(예: 런타임 시 생성된 이름 반환).
마크업 텍스트 (또는 데이터 저장소 마크업)
데이터 저장소 마크업 텍스트는 데이터 저장소 필드와 공급자를 UI나 타 시스템에서 참조하는 방식으로, HTML 태그와 유사하게 보입니다. 마크업 구문은 Markup Syntax을 참조하십시오.
가상 data 저장소
존재하지 않으나(즉 데이터 저장소 클라이언트에 등록되지 않음) 데이터 저장소 마크업 파싱 코드(예: Attributes 데이터 저장소)로 인식되는 데이터 저장소입니다.

구성 요소


데이터 저장소 시스템에는 데이터 저장소 클라이언트, 데이터 저장소, 데이터 공급자, 데이터 구독자/게시자 등의 4가지 기본 구성 요소가 있습니다. 데이터 저장소 시스템에서 각 구성 요소의 역할에 대한 설명은 다음과 같습니다.

데이터 저장소 클라이언트

데이터 저장소 클라이언트는 데이터 저장소의 등록 및 수명 관리를 담당하고, UI 및 기타 시스템에 등록된 데이터 저장소 액세스를 공급하는 싱글톤 오브젝트입니다. 데이터 저장소 클라이언트는 게임이 실행하는 동안 존속됩니다.

셋업 및 초기화

데이터 저장소 클라이언트 생성에 사용되는 클래스는 Engine.DataStoreClientClassName 등록 정보의 값으로 결정됩니다. 기본적으로 사용되는 데이터 저장소 클래스는 Engine.DataStoreClient 이나 게임의 DefaultEngine.ini에서 [Engine.Engine] 섹션의 DataStoreClientClassName 등록 정보 값을 변경하여 바꿀 수 있습니다. 이 등록 정보의 값이 무효한 경우(공백, DataStoreClient의 하위 클래스에 해당하지 않는 경우 등) 대신 기본 데이터 저장소 클라이언트 클래스가 사용됩니다(Engine.DataStoreClient). 구성된 DataStoreClientClassNameUEngine::InitializeObjectReferences() 에 로드되며(동적 로드 오브젝트가 탐색 없는 로드와 호환되는 드문 경우 중 하나) 클래스 참조는 Engine.DataStoreClientClass 등록 정보에 저장됩니다. 데이터 저장소 클라이언트는 엔진 초기화 중 UI 컨트롤러(UIInteraction)가 UUIInteraction::InitializeGlobalDataStores() 에 생성합니다. 이후 루트 세트에 추가되며(따라서 가비지 컬렉션이 수행되지 않음) 데이터 저장소 클라이언트에 등록된 모든 전역 데이터 저장소(다음 섹션 참조)를 생성 및 초기화합니다.

작업

데이터 저장소와의 모든 초기 상호 작용은 데이터 저장소 클라이언트를 거쳐야 합니다. 데이터 저장소 클라이언트는 데이터 저장소 인스턴스 생성, 새 데이터 저장소 등록, 태그를 통해 등록된 데이터 저장소 검색, 등록된 데이터 저장소에 다양한 통지 전달 등을 위한 메소드를 제공합니다. 데이터 저장소 클라이언트는 전역 데이터 저장소와 플레이어 데이터 저장소 등의 두 가지 데이터 저장소 목록을 유지합니다.

전역 데이터 저장소

데이터 저장소는 공급하는 데이터가 특정 플레이어 고유의 것이 아닌 경우 전역 데이터 저장소로 고려됩니다. 맵 이름, 캐릭터 이력 등의 정적 게임 리소스와 가용 인터넷 게임 호스트 등의 원격 데이터, 현재 게임 점수 한도 등의 동적 데이터는 전역 데이터 저장소가 제공하는 데이터 유형의 예입니다. 데이터 저장소 클라이언트가 초기화될 때는(UDataStoreClient::InitializeDataStores()) GlobalDataStoreClasses 배열에 나열된 각 전역 데이터 저장소의 단일 인스턴스가 생성되고(전역 데이터 저장소 등록과 관련한 지침은 data store registration 섹션 참조) 이후 RegisterDataStore() 를 호출하여 데이터 저장소 클라이언트에 등록됩니다. 각 전역 데이터 저장소에는 성공적인 등록을 위해 고유한 데이터 저장소 태그가 있어야 합니다. 성공적으로 등록된 후에는 게임 종료 시까지 전역 데이터 저장소(기본값)가 메모리에 상주합니다. 전역 데이터 저장소는 모든 시스템에서 데이터 저장소 클라이언트의 FindDataStore() 메소드를 호출하고 필요한 데이터 저장소 태그를 전달하여 액세스할 수 있습니다.

플레이어 데이터 저장소

데이터 저장소는 공급하는 데이터가 특정 플레이어 고유의 것인 경우 플레이어 데이터 저장소로 고려됩니다. 컨트롤러 기본 설정, 선호 무기 등과 같은 플레이어 특정 구성, 친구 목록 등의 원격 데이터, 현재 상태 등의 동적 데이터는 플레이어 데이터 저장소로 제공되는 데이터 유형의 예입니다. 플레이어 데이터 저장소와 전역 데이터 저장소 사이에는 다음과 같이 매우 중요한 몇 가지 차이점이 있습니다.

  • 플레이어 데이터 저장소는 엔진 초기화 시점이 아니라 새 플레이어가 생성될 때만 만들어집니다.
  • 플레이어 데이터 저장소는 게임 수명 동안 지속되지 않으며 플레이어가 게임에서 제거되면(예: 분할 화면 대결에서 두 번째 플레이어가 로그아웃하는 경우) 해당 플레이어에 대한 모든 플레이어 데이터 저장소가 등록 해제되고 그에 따라 가비지 컬렉션이 수행됩니다.
  • 각 플레이어에 대해 각 플레이어 데이터 저장소의 단일 인스턴스가 생성됩니다. 플레이어 데이터 저장소 태그는 단일 플레이어에 대한 플레이어 데이터 저장소 목록에서 고유해야 합니다.
  • 따라서 각 플레이어마다 특정 태그가 있는 데이터 저장소가 있습니다. 예를 들어 'PlayerSettings' 태그가 있는 플레이어 데이터 클래스를 등록했고 게임에 세 플레이어가 있는 경우 'PlayerSettings' 태그가 있는 데이터 저장소 인스턴스 3개가 있게 됩니다. 이를 통해 마크업 태그가 리졸브를 수행할 플레이어 데이터 저장소를 표시하는 정보를 포함할 필요 없이 플레이어 데이터 저장소에 해당하는 데이터 저장소 마크업 태그를 간편하게 리졸브할 수 있습니다.

데이터 저장소 클라이언트가, 새 플레이어가 생성되었다는 통지를 받으면 등록된 플레이어 데이터 저장소 클래스의 목록을 반복하고(플레이어 데이터 저장소 등록에 대한 지침은 data store registration 섹션 참조) 각 클래스의 인스턴스를 생성합니다. 그런 다음 호출에서 새 플레이어에 대한 참조를 RegisterDataStore() 로 전달하여 새 인스턴스를 플레이어 데이터 저장소로 등록합니다. 새 요소가 PlayerDataStores 배열에 추가됩니다. 이 배열은 새로 생성된 플레이어와 해당 플레이어의 플레이어 데이터 저장소에 대한 참조를 포함합니다. 데이터 저장소 클라이언트가, 플레이어가 제거되었다는 통지를 받으면 PlayerDataStores 배열을 반복하여 제거되는 플레이어와 일치하는 PlayerOwner가 있는 요소를 검색합니다. 제거된 플레이어에 대한 PlayerDataStoreGroup을 찾으면 해당 플레이어에 대한 모든 플레이어 데이터 저장소를 등록 해제하고 PlayerDataStores 배열에서 요소를 제거합니다. 플레이어 데이터 저장소는 모든 시스템에서 데이터 저장소 클라이언트의 FindDataStore() 메소드를 호출하고 플레이어에 대한 참조와 필요한 데이터 저장소 태그를 전달하여 액세스할 수 있습니다.

데이터 공급자

데이터 공급자(Engine.UIDataProvider)는 데이터 저장소 시스템의 기본 구성 요소입니다. 데이터 공급자는 UI와 게임 간의 게이트웨이 역할을 수행하여 UI가 데이터의 실제 출처를 알지 않고도 데이터를 게시 및 수신할 수 있도록 합니다. 데이터 공급자는 데이터 필드를 사용하여 타 시스템에 데이터를 공개합니다. 즉 데이터 필드 유형을 나타내는 값과 함께 지원하는 데이터 공급자가 지원하는 일부 데이터를 고유하게 식별하는 태그가 포함된 구문(UIDataProvider.UIDataProviderField) 입니다. 데이터 필드 태그(UIDataProviderField.FieldTag)는 데이터 공급자가 제공하는 데이터의 "별칭" 으로 간주할 수 있습니다. 각 데이터 필드 태그는 중첩 데이터 공급자(또한 중첩 데이터 공급자의 배열) 또는 일부 게임 내 실제 데이터 값 중 하나를 매핑합니다. 데이터 값 유형은 FieldType (UIDataProviderField.FieldType) 값으로 지정합니다. 현재 데이터 저장소 시스템에서 지원하는 유형은 정수, 부울 또는 문자열 등의 단순 유형과, 현재 값, 최소값, 최대값으로 구성된 범위 값, 데이터 배열, 중첩 데이터 공급자 배열 등입니다(EUIDataProviderFieldType 목록 참조).

특정 데이터 공급자에서 지원하는 데이터 필드 태그 목록은 UI 에디터의 데이터 저장소 브라우저에 표시되는 항목을 결정합니다(데이터 필드 태그를 위젯을 통한 바인딩에 사용할 수 있게 함). 데이터 필드 태그는 전적으로 임의적인 것이며 데이터 공급자가 적합하다고 간주하는 방식으로 생성될 수 있습니다. 사실상 각 데이터 공급자 클래스는 사용 가능한 데이터 필드 태그의 목록을 생성하기 위해 다양한 메소드를 사용할 수 있습니다. 일부 데이터 공급자는 클래스의 구성원 등록 정보를 사용하여 태그 목록을 생성하며(데이터 공급자 클래스 또는 게임 내 일부 다른 클래스) 또 다른 공급자는 프로그래머가 defaultproperties나 ini에 입력한 이름의 배열을 사용할 수도 있습니다. 또한 데이터 필드 태그를 하드코딩하는 데이터 공급자도 있을 수 있습니다. 데이터 공급자가 지원되는 태그 목록을 생성하는 방법은 데이터 공급자가 공개하는 데이터 값을 저장하는 방식과 위치에 따라 결정됩니다. 요약하자면 각 데이터 공급자는 원하는 방식으로 데이터 필드 태그 목록을 생성할 수 있고 모든 데이터 공급자는 UI가 해당 공급자가 처리하는 데이터를 참조하는 데 사용할 수 있는 태그 목록을 UI에 제공해야 합니다.

데이터 공급자는 데이터 값을 검색 및 수정할 수 있습니다. 허용되는 트랜잭션 유형은 데이터 공급자의 WriteAccessType 필드에서 결정됩니다. 이 필드의 값이 ACCESS_ReadOnly 일 경우 데이터 공급자가 공개하는 데이터를 수정할 수 없습니다.

데이터 저장소

데이터 저장소는 데이터 저장소 시스템과의 상호 작용을 위한 최상위 공개 인터페이스입니다. 데이터 저장소는 타 시스템이 데이터 유형을 모르고도 게임 데이터와 상호 작용할 수 있게 하며 참조와 수명 관리를 캡슐화하여 타 시스템이 오브젝트 참조 보유, 포인터 불일치, 초기화되지 않은 데이터 및 기타 가비지 컬렉션이나 메모리 용량 증대를 초래하는 문제에 대해 우려하지 않고도 자유롭게 데이터와 상호 작용할 수 있도록 합니다. 데이터 저장소는 문자열, 이미지, 기본 유형(정수, 부울, 부동 소수점 등), 배열, 구문, 특정 오브젝트 등의 렌더링 가능한 모든 유형의 데이터를 처리할 수 있습니다. 더 복잡한 유형의 경우 해당 데이터를 제공하도록 설계된 데이터 저장소에서 맞춤식으로 처리해야 합니다. 데이터 저장소에 포함된 데이터 유형에 관계없이 데이터의 바인딩, 검색 및 게시를 위한 인터페이스는 동일합니다.

데이터 공급자의 목적은 특정 정적 리소스나 게임플레이 오브젝트 인스턴스에 대한 데이터를 추적하는 것이지만 데이터 저장소는 주로 타 데이터 공급자의 집합 역할을 하도록 설계되었습니다. 데이터 저장소는 관련 데이터 공급자의 그룹에 대한 중앙 위치를 제공하는 데 사용하는 것이 가장 적합하며 이런 식으로 볼 때 디렉터리 구조의 폴더와 매우 흡사하게 생각할 수 있습니다. 데이터 저장소에 포함된 데이터 공급자는 일반적인 데이터 저장소 필드에서와 마찬가지로, 데이터 저장소가 제공한 데이터 저장소 마크업 문자열 형식의 임의 태그를 사용하여 액세스합니다.

데이터 저장소에는 영구 데이터 저장소, 씬 데이터 저장소 및 의사 데이터 저장소 등의 3가지 유형이 있습니다.

영구 데이터 저장소는 데이터 저장소 클라이언트에 등록되며 UI 내 모든 씬(또는 타 시스템)의 위젯을 통해 액세스할 수 있습니다. 영구 데이터 저장소는 리소스 데이터(게임 유형 또는 맵 설명), 원격 데이터(원격 게임 호스트 정보), 상태 데이터(예: 점수, 시간 제한) 등의 정보를 추적합니다. 전역 및 플레이어 데이터는 위의 데이터 저장소 클라이언트 섹션에서 언급한 클래스를 저장하며 모두 영구 데이터 저장소로 간주됩니다.

임시 데이터 저장소는 특정 UIScene을 통해 생성 및 연결되며 씬이나 하위 씬의 위젯을 통해서만 액세스할 수 있습니다. 임시 데이터 저장소는 씬의 편집 상자에서 입력한 값과 같은 정보를 추적합니다. 보류 중인 연결에 대한 정보도 여기에 해당할 수 있습니다.

의사 데이터 저장소는 실제로는 존재하지 않는 데이터 저장소입니다. 즉 데이터 저장소를 태그(마크업을 통해) 참조할 수는 있지만 해당 데이터 저장소에 해당하는 데이터 저장소 클래스는 없습니다. 현재는 의사 데이터 저장소는 Attributes 데이터 저장소 하나입니다. 이 저장소는 인라인 수정(예: 굵음, 기울임꼴 등)을 UI에서 렌더링하는 데이터에 적용하는 기능을 제공합니다.

데이터 구독자 / 게시자

데이터 구독자는 데이터 저장소 시스템에서 데이터를 검색하는 오브젝트로 정의됩니다. 반면 게시자는 데이터를 데이터 저장소 시스템에 게시하는 오브젝트입니다. 데이터 저장소 구독자가 되려면 클래스가 Engine.UIDataStoreSubscriber 인터페이스에 구현되어야 합니다. 이 인터페이스는 데이터 저장소 시스템에서 데이터를 검색하는 데 사용하는 메소드를 선언합니다. 반대로 데이터 저장소 게시자의 경우 클래스가 Engine.UIDataStorePublisher 인터페이스 클래스를 구현해야 합니다. UIDataStorePublisher 인터페이스는 UIDataStoreSubscriber 인터페이스의 하위입니다. 따라서 UIDataStorePublisher 인터페이스를 구현하는 모든 클래스는 자동으로 데이터 저장소 구독자가 되므로 이러한 메소드도 함께 구현해야 합니다.

데이터 저장소 구독자는 데이터 저장소 바인딩을 통해 지원하는 데이터 필드 구독 수를 정의합니다(UIDataStoreSubscriber.UIDataStoreBinding). 이 구문은 단일 데이터 필드 태그에 대한 다양한 정보를 추적하며 데이터 저장소 참조에 대한 데이터 저장소 마크업 문자열 리졸브(ResolveMarkup, Un/RegisterSubscriberCallback), 데이터 저장소로부터 값 검색 및 게시(GetBindingValue/SetBindingValue)를 캡슐화합니다. 데이터 저장소 바인딩은 모든 데이터 저장소 필드에 사용할 수 있으며 보통은 하나의 데이터 저장소 바인딩만 필요하지만 필요한 만큼 만들 수도 있습니다.

UIDataStoreSubscriber 인터페이스가 선언하는 메소드 중 하나는 RefreshSubscriberValue() 메소드입니다. 데이터 저장소 바인딩이 활성화되면(바인딩의 마크업 문자열을 데이터 저장소 참조에 리졸브) 구독자의 RefreshSubscriberValue 메소드가 리졸브된 데이터 저장소의 구독자 콜백 목록에 추가되므로 데이터 필드의 값이 수정되면 구독자가 통지를 받게 됩니다. 자세한 내용은 Notifications 를 참조하십시오.

마크업 구문


데이터 저장소 마크업 구문은 HTML과 매우 유사한 간단한 형식입니다. 마크업 텍스트는 각괄호 안에 들어가야 하며 데이터 저장소 태그와 데이터 필드에 대한 "경로" 등이 두 가지 기본 섹션으로 나뉩니다. 데이터 저장소 태그는 데이터 찾기에 사용할 데이터 저장소를 결정하는 데 사용하며 항상 뒤에 콜론이 옵니다. "데이터 필드 경로" 는 데이터 저장소의 특정 데이터 필드 액세스에 사용되는 데이터 필드 태그의 순서(또는 내부 데이터 공급자 중 하나)입니다. 데이터 필드 경로는 데이터 저장소나, 데이터 저장소 안에 포함된 중첩 데이터 공급자가 공개한 데이터 필드 태그에 해당하는 하나 이상의 "노드" 를 포함합니다. 여러 노드는 마침표로 구분하며 배열 인덱스는 세미콜론으로 구분합니다.

선호 무기, 컨트롤러 레이아웃 등과 같은 일부 사용자 설정에 대한 액세스를 제공하는 데이터 저장소가 있다고 가정해 보겠습니다. 여기서 이 데이터 저장소의 태그는 'Settings' 입니다. 이 설정 데이터 저장소는 PreferredWeapon 및 Controls 등의 두 데이터 필드를 지원합니다. PreferredWeapon은 플레이어가 선호하는 무기의 이름을 담은 단순 문자열을 나타내며 Controls 태그는 내부 데이터 공급자에 해당합니다. Controls 데이터 공급자는 Sensitivity 및 InvertY 등의 두 필드를 제공합니다. 여기서 Sensitivity는 두 요소(각 축에 하나씩)를 포함하는 배열을 나타내는 태그이고 InvertY는 플레이어의 Y축이 전환되는지 여부를 결정하는 부울 값을 나타내는 태그입니다. 따라서 Settings 데이터 저장소에 대한 레이아웃은 다음과 유사할 것입니다.

  • Settings
    • PreferredWeapon
    • Controls
      • Sensitivity[2]
      • InvertY

PreferredWeapon 데이터 태그를 참조하려면 마크업 문자열이 다음과 유사하게 됩니다. <Settings:PreferredWeapon>
Controls 데이터 공급자에서 InvertY 데이터 태그를 참조하려면 마크업 문자열이 다음과 유사하게 됩니다. <Settings:Controls.InvertY> .  InvertY 데이터 필드 태그는 Controls 내부 데이터 공급자에서만 지원되므로 Controls 데이터 공급자를 통해서만 액세스할 수 있습니다. 마크업 문자열 <Settings:InvertY> 는  작동하지 않습니다. Settings 데이터 저장소가 PreferredWeapon 및 Controls 등의 두 데이터 필드 태그만 지원하기 때문입니다.
Sensitivity 배열의 첫 번째 요소를 참조하려면 마크업 문자열이 다음과 유사하게 됩니다. <Settings:Controls.Sensitivity;0>   (마크업 배열 요소 인덱스는 코드에서처럼 0기반 입니다.)

내부 데이터 공급자의 "범위" 안에 있게 되면 마크업 문자열의 다음 노드에 대해서는 해당 내부 데이터 공급자가 지원하는 데이터 필드 태그만 유효합니다. 예를 들어 다음 마크업 문자열은 성공적으로 리졸브되지 않습니다. <Settings:Controls.PreferredWeapon>  "Controls" 노드가 파싱된 후에는 다음 노드가 Controls 데이터 공급자의 컨텍스트를 통해 리졸브됩니다.

카테고리


데이터 저장소는 몇 가지 일반적인 카테고리로 구분됩니다. 이러한 카테고리는 데이터 저장소가 표시하는 데이터 유형 또는 데이터 값이 존재하는 위치에 따라 구분됩니다. 이러한 카테고리는 데이터 저장소 시스템의 클래스 계층 구조와 무관하며 정적 데이터 저장소는 동적 데이터 저장소 등에서 파생될 수 있기 때문에 이러한 범주는 데이터 저장소가 해결하는 문제의 유형을 이해 및 분류하는 수단으로 활용할 목적이 더 큽니다.

정적(또는 리소스) 데이터 저장소

정적 데이터 저장소는 게임 실행 중 변경되지 않는 데이터를 공개하는 데이터 저장소입니다. 정적 데이터 저장소는 일반적으로 업데이트된 값을 데이터 필드에 게시하는 것을 허용하지 않습니다. 표시한 데이터가 근본적으로 정적이기 때문입니다. 이러한 데이터 저장소 유형에서 사용 가능한 필드 목록은 일반적으로 변경되지 않습니다. 이러한 데이터 저장소는 보통 런타임 시 게임플레이 오브젝트 인스턴스와의 연계나 수명 관리를 지원하는 추가적인 코드를 요구하지 않기 때문에 게임플레이 설정이 간편합니다.

이러한 데이터 저장소는 다음과 같은 데이터를 공개합니다.

  • 캐릭터 정보(이력, 최대 건강, 공격도, 활용 가능한 무기, 메쉬, 스킨 등)
  • 맵 데이터(가용 맵, 맵 이름, 맵 지원 플레이어, 설명, 스크린샷 등)
  • 현지화된 문자열
  • 텍스처 또는 재질에 대한 액세스

정적 데이터 저장소 예

  • Engine.UIDataStore_Strings(이 데이터 저장소가 지원하는 필드는 에디터에서 수정이 가능하기 때문에 에디터에서 이 데이터 저장소는 가변 데이터 저장소로 간주됨)
  • Engine.UIDataStore_GameResource

동적(또는 상태) 데이터 저장소

동적 데이터 저장소는 게임 중에 변경되는 데이터를 공개하는 데이터 저장소입니다. 동적 데이터 저장소는 데이터 저장소의 용도에 따라 데이터 필드에 업데이트된 값을 공개하는 것을 허용하거나 허용하지 않을 수 있습니다. 이러한 데이터 저장소 유형에서 사용 가능한 필드 목록은 일반적으로 변경되지 않습니다. 게임에서 사용되는 대부분의 데이터 저장소는 보통 동적 데이터 저장소이며 이러한 데이터 저장소가 사용하는 데이터 공급자는 거의 항상 게임 내 오브젝트의 특정 인스턴스와 연결됩니다.

이러한 데이터 저장소는 다음과 같은 데이터를 공개합니다.

  • 게임 상태(연결된 플레이어 수, 가능한 픽업, 남은 시간, 남은 목표 등)
  • 플레이어 상태(현재 상태, 탄약, 장착 무기, 현재 진행 등)
  • 원격 세션 데이터(가용 온라인 세션, 친구 목록, 연결 정보, 세션 검색 결과 등)
  • 구성 가능한 설정 데이터(예: 플레이어 기본 설정, 호스팅 기본 설정 등)

동적 데이터 저장소 예

  • Engine.UIDataStore_OnlinePlayerData
  • Engine.UIDataStore_OnlineStats
  • Engine.UIDataStore_OnlineGameSearch
  • Engine.UIDataStore_Settings 및 그 하위 클래스
  • Engine.UIDataStore_GameState 및 그 하위 클래스

변수 데이터 저장소

변수 데이터 저장소는 데이터 저장소가 사용할 수 있는 필드 목록의 런타임 수정을 허용하는 데이터 저장소입니다. 이러한 데이터 저장소는 항상 데이터 필드에 업데이트된 값을 게시하도록 허용하며 일반적으로 여러 씬 간에 또는 디자이너를 위한 "스크래치패드" 로 공유가 필요하거나, 데이터 저장소가 공개한 데이터 필드가 일부 일관되지 않은 데이터 집합(예: 특정 클래스의 인스턴스)에 해당하는 경우 임의 데이터를 저장하는 데 사용됩니다.

변수 데이터 저장소 예

  • Engine.UIDataStore_Fonts
  • Engine.UIDataStore_Images
  • Engine.SceneDataStore

데이터 저장소 생성


데이터 저장소를 생성하려면 설계, 구현, 테스트 등의 3단계가 필요합니다. 먼저 각 단계와 관련한 작업을 개괄적으로 설명한 다음 각 작업을 다시 상세히 살펴보겠습니다.

첫 번째 단계인 설계는 다음과 같은 작업으로 구성됩니다.

  1. 데이터 유형 - 먼저 이 데이터 저장소가 공개할 데이터 유형을 결정해야 합니다. 이 데이터 저장소가 정적 데이터, 동적 데이터 또는 가변 데이터를 공개할 것입니까?
  2. 구성 요소 - 다음으로 데이터 저장소의 책무를 구분하는 방법을 결정합니다. 이 데이터 저장소가 데이터에 대한 직접 액세스를 제공합니까, 여러 데이터 공급자 또는 두 가지의 조합으로 구성됩니까? 이 데이터 저장소가 얼마나 많은 데이터 공급자를 필요로 합니까? 이 데이터 공급자의 책무는 무엇입니까? 이 데이터 저장소가 단일 리소스, 단일 클래스 유형의 여러 인스턴스 또는 여러 클래스 유형의 여러 인스턴스에 대해 데이터를 공개합니까?
  3. 데이터 필드 생성: 다음으로 데이터 필드 태그의 목록을 생성하는 방법을 결정합니다. 이 데이터 저장소에 가장 적합한 방법은 무엇입니까? 하드코드 목록 또는 데이터 기반이 적합합니까? 생성된 중첩 데이터 공급자를 기반으로 동적으로 생성됩니까?
  4. 저장: 다음으로 이 데이터가 공개한 데이터의 값이 존재할 위치를 파악합니다. 데이터 저장소(또는 그 공급자)가 값 자체를 저장합니까, 또는 데이터 저장소가 게임플레이 오브젝트의 클래스처럼 이미 어딘가에 존재하는 데이터를 단순히 표시합니까?
  5. 표현: 마지막으로 데이터 필드의 목록이 사용자에게 제시되는 방법을 결정합니다(예: 데이터 저장소 브라우저에 표시). 중첩 데이터 공급자를 사용할 경우 데이터 저장소가 단순히 중첩 데이터 공급자에 대한 액세스를 제공하는 태그를 공급합니까? 데이터 저장소가 중첩 공급자의 데이터 필드를 포맷하여 데이터 공급자가 사용자에게 투명하도록 해야 합니까?

다음 단계는 구현입니다:

  1. 클래스 생성: 먼저 데이터 저장소와 지원 클래스를 생성합니다.
  2. 등록: 다음으로 런타임 시 생성되도록 데이터 저장소 클라이언트에 새 데이터 저장소를 등록해야 합니다.
  3. 메소드: 마지막으로 데이터 저장소가 작동하도록 메소드를 구현해야 합니다.

마지막으로 테스트 씬 셋업입니다:

  1. 테스트 씬 셋업: 먼저 데이터 저장소의 모든 메소드가 올바르게 작동하도록 간편하게 테스트하는 데 사용할 테스트 씬을 설정합니다.
  2. 위젯 바인딩: 다음으로 테스트 씬에서 위젯에 대한 바인딩을 설정합니다.

설계

작성 예정

1단계: 데이터 유형

이 단계에서는 이름에서 짐작할 수 있듯 데이터 저장소가 공개할 데이터 유형을 합니다. 데이터 저장소가 공개할 데이터 유형을 결정하면 데이터 저장소 구성 방법, 데이터 저장소의 defaultproperties 블록에서 설정해야 하는 값, 구현할 메소드 등을 결정하는 데 도움이 됩니다. 데이터 저장소가 집합을 포함할 경우 데이터 저장소 클래스가 UIListElementProvider 인터페이스를 구현해야 합니다.

동적 데이터를 표현할 데이터 저장소의 경우 구독자가 데이터 값을 변경할 수 있습니까? 가능할 경우 데이터 저장소의 WriteAccessType 값을 ACCESS_ReadOnly 가 아닌 값으로 설정하고 메소드의 SetDataField 패밀리를 구현해야 합니다. 가변 데이터를 표시하는 데이터 저장소의 경우 UIDynamicFieldProvider (또는 이 클래스의 자체 하위)를 데이터 저장소의 내부 데이터 공급자로 사용해야 합니다. 이 클래스는 이미 가변 데이터 저장소 기능을 지원하는 데 필요한 함수를 구현했기 때문입니다.

2단계: 구성 요소

이 단계는 데이터 저장소 클래스 설계의 핵심입니다. 데이터 저장소 시스템은 정해진 것이 없기 때문에 데이터 저장소를 구성하는 방법에는 여러 옵션이 있습니다. 이 때문에 원하는 모든 데이터 흐름 유형을 표현할 수 있다는 장점이 있지만 제대로 정의된 경계가 부족하기 때문에 시작할 지점이나 나아갈 방향을 결정하는 것이 어려울 수 있습니다. 데이터 저장소(또는 데이터 공급자)를 설계할 때는 다음과 같은 3가지 기본 구조를 사용할 수 있습니다.

  1. 플랫 데이터 저장소 - 데이터 저장소 자체가 자체 데이터 공급자 역할을 하며 처리할 중간 단계가 없습니다. 이 구조에서는 데이터 저장소가 지원하는 모든 데이터 필드가 데이터에 해당합니다. 데이터 저장소는 모든 데이터 값의 검색 및 게시를 담당합니다.
    • 장점
      • 작성, 검토, 유지 관리가 간편합니다. 한 클래스에서 데이터 필드 생성, 데이터 값 검색 및 게시를 수행하므로 코드를 따르고 유지 관리하기가 훨씬 쉽습니다.
      • 효율성 - 모든 데이터 필드가 데이터 저장소 자체에 포함되므로 마크업 문자열을 데이터 값으로 파싱하는 전체 프로세스가 훨씬 빠릅니다.
    • 단점
      • 다른 데이터 저장소에서 이 데이터 저장소의 기능을 재활용할 방법이 없습니다.
      • UI 에디터의 데이터 저장소 브라우저에서 지원되는 데이터 필드는 데이터 저장소의 가용 데이터 필드 탐색을 어느 정도 광범위하게 할 수 있는 플랫 목록으로 표시됩니다.
    • 용도 이 구조의 유형은 현지화된 문자열, 이미지에 대한 직접 액세스(즉 경로 이름을 사용하여 이미지 참조) 등과 같이 매우 특정한 데이터 유형을 표시하는 데이터 저장소에 가장 적합합니다.

  1. 복합 데이터 저장소 - 이 데이터 저장소는 단순히 중첩 데이터 공급자에 액세스하는 포털입니다. 이 구조에서는 모든 데이터 필드가 내부 데이터 공급자에 해당하며 이러한 데이터 공급자가 실제 데이터에 대한 액세스를 제공합니다. 데이터 저장소는 데이터 공급자의 생성 및 관리를 담당하고 일부 경우 어느 정도의 수명 관리도 처리합니다(특히 레벨 변경에서의 정리). 데이터 저장소의 공급자는 자체 내부 데이터 공급자를 갖거나 갖지 않을 수 있습니다.
    • 장점
      • 모든 데이터 검색 및 게시는 개별 데이터 공급자에게 위임되며 데이터 저장소의 코드가 매우 간단하게 남습니다.
      • 단일 데이터 저장소 "우산" 에서 관련 데이터의 그룹을 액세스할 수 있습니다.
      • 의무 및 책무 구분이 매우 간편하고, 기존 클래스의 재사용이 증대되며 유지 관리 비용을 줄일 수 있습니다.
      • UI 에디터의 데이터 저장소 브라우저에서 데이터는 특정 데이터 필드를 신속하게 찾을 수 있도록 카테고리 아래 표시됩니다.
    • 단점
      • 추상으로 인해 추가적인 간접 수준이 생기므로 클래스 작동 방법을 이해하기 위해 코드 추적이 더 크게 요구됩니다.
      • 구현이 여러 클래스에 걸쳐 수행되어 유지 관리 중 버그가 발생할 가능성이 높아집니다.
      • UI 에디터의 데이터 저장소 브라우저에서 특정 데이터 필드를 탐색하려면 더 많은 클릭이 필요합니다.
    • 용도 이 구조 유형은 '메타 데이터 저장소"의 일종인 많은 관련 데이터 그룹에 대한 액세스를 제공하는 데이터 저장소에 가장 적합합니다. 이 구조는 데이터 저장소가 게임플레이 관련 클래스의 여러 인스턴스에 있는 데이터에 대한 액세스를 제공해야 할 경우에 선호됩니다(예: Engine.PlayerReplicationInfo). 일반적인 데이터 저장소 구조입니다.

  1. 조합 데이터 저장소 - 데이터 저장소는 두 가지 다른 유형의 조합입니다. 데이터 필드 중 일부는 실제 데이터를, 다른 필드는 중첩 데이터 공급자를 나타냅니다.
    • 분명히 이 구조에는 다른 두 유형의 장점과 단점이 모두 있으므로 이에 대해서는 다시 언급하지 않겠습니다.
    • 용도: 이 구조는 임의 게임플레이 클래스에 대해 데이터를 표시하는 중첩 데이터 공급자에 가장 적합합니다. 여기서 게임플레이 클래스에는 다른 데이터 공급자 클래스를 지원하는 유형의 구성원 변수가 있습니다. 예를 들어 Engine.PlayerDataProvider 클래스는 Engine.PlayerReplicationInfo 클래스의 데이터 바인딩 등록 정보를 공개합니다. Engine.TeamDataProvider 클래스는 Engine.TeamInfo 클래스의 데이터 바인딩 등록 정보를 공개합니다. PlayerReplicationInfo 는 표시된 데이터 바인딩이 아닌 TeamInfo 변수를 포함합니다. 그러나 플레이어에 대한 데이터 공급자를 통해 플레이어의 팀에 대해 데이터 공급자를 액세스하는 기능을 제공하고자 했으므로 TeamInfo 변수에 데이터 바인딩 키워드를 추가하고 PlayerDataProvider 가 플랫 데이터 공급자가 아닌 조합 데이터 공급자가 되었습니다.

3단계: 데이터 필드 목록 생성

지원되는 데이터 필드의 목록을 구성하는 것은 보통 처음 두 데이터 저장소를 생성할 때 가장 헷갈리는 단계 중 하나입니다. 그러나 가장 특징적인 부분만 파악하고 나면 프로세스에서 가장 쉬운 단계이기도 합니다. 근본적으로 바인딩되지 않는다는 점이 이 단계의 처음에 혼동을 초래하는 가장 큰 요인이 될 것입니다. 즉 "지원되는 데이터 필드 목록을 어떻게 생성합니까?" 라는 질문에 대해 가장 정확한 답은 "원하는 대로" 와 비슷할 것입니다. 물론 질문한 사람에게는 그다지 도움이 되지 않는 답입니다. 따라서 기본적인 내용을 먼저 살펴보고 "원하는 대로"가 무엇인지 살펴보도록 하겠습니다. 간단한 데이터 저장소를 예로 들겠습니다.

지원되는 데이터 필드 목록을 데이터 공급자로부터 검색하는 메소드는 UUIDataProvider::GetSupportedDataFields() 메소드입니다. 이 메소드는 단일 매개 변수로 UIDataProviderFields 배열에 대한 참조를 취하며 데이터 공급자가 지원하는 데이터 필드 목록으로 이 배열을 채운다고 예상합니다. UIDataProviderField 는 이름과 유형으로 구성된 구문입니다(다른 필드인 FieldProviders 도 있으나 잠시 무시함). 데이터 공급자가 지원하는 각 데이터 필드에 대해 해당 필드의 태그와 유형을 포함하는 배열에 하나의 요소를 추가합니다. 배열에 추가되는 데이터 필드는 데이터 저장소 자체에서 지원하는 데이터 필드여야 합니다. 자체 데이터 필드를 포함하는 내부 데이터 공급자를 포함하는 경우에도 마찬가지입니다. 몇 가지 아주 간단한 예를 살펴보겠습니다.

매우 간단하고 특수한 데이터 저장소를 만든다고 가정하겠습니다. 이 저장소의 유일한 목적은 현재 감마 설정에 대한 직접적인 읽기/쓰기 액세스를 제공하는 것입니다. 감마가 사용하는 실제 감마 값은 UClient::DisplayGamma 로, 이 데이터 저장소는 자체 등록 정보를 가질 필요가 없습니다. 단 하나의 데이터 필드만 지원하며 이 데이터 필드의 태그로 "Gamma" 를 사용하기로 결정했습니다. 이 데이터 필드는 집합을 나타내거나 내부 데이터 공급자에 해당하지 않으며 단일 데이터 값을 나타내므로 이 데이터 필드의 유형은 DATATYPE_Property 여야 합니다. 새 데이터 저장소의 GetSupportedDataFields() 메소드는 "Gamma" 태그 및 DATATYPE_Property 필드 유형을 갖는 배열에 단일 요소를 추가합니다. 아주 간단합니다. 그리고 - Engine.UIDataStore_Gamma 의 코드를 직접 확인한다면 이 데이터 저장소는 사실상 이미 존재합니다.

지원되는 데이터 필드 목록 구성을 위해 전적으로 다른 메소드를 사용하는 다른 데이터 저장소인 글꼴 데이터 저장소(Engine.UIDataStore_Fonts)를 살펴보겠습니다. 이 데이터 저장소는 포함된 마크업을 사용하여 문자열 중간에서 글꼴을 교체할 수 있도록 하는 문자열 렌더링 코드에서 사용합니다(예: TestUIScenes.upk 패키지의 TestFontScene 참조). UObject를 안정적으로 참조하는 유일한 방법은 경로 이름을 사용하는 것이므로 이 데이터 저장소가 액세스를 제공하는 데이터는 메모리에 있는 모든 유효한 UFont 오브젝트에 대한 경로 이름입니다. 필드의 "값"은 단순히 문자열이므로 이 필드의 유형도 DATATYPE_Property 입니다. 따라서 UIDataStore_Fonts::GetSupportedDataFields() 메소드에서 메모리의 각 글꼴 오브젝트에 대해 하나의 데이터 필드를 추가하고 DATATYPE_Property 를 데이터 필드 유형으로 합니다.

마지막으로 좀 더 복잡한 데이터 저장소인 현재 감마 데이터 저장소(Engine.CurrentGameDataStore)를 살펴보겠습니다. 이 데이터 저장소에는 게임의 모든 PlayerReplicationInfoTeamInfo 오브젝트에 데이터를 제공하는 내부 데이터 공급자의 두 집합이 있습니다(GameData 필드가 처리되는 방법 때문에 지금은 이것을 무시하겠으나 나중에 아래 표현 섹션에서 다룰 것입니다). 이 두 집합에 액세스하기 위해 사용하기로 결정한 태그는 각각 "Players"와 "Teams" 입니다. 데이터 공급자의 집합이므로 두 필드의 유형은 DATATYPE_ProviderCollection 입니다. 따라서 UCurrentGameDataStore::GetSupportedDataFields() 메소드에서 두 요소[*GameData* 필드 포함 안 함]를 출력 배열에 추가합니다. 첫 번째 요소의 태그는 "Players" 이고 두 번째 요소의 태그는 "Teams" 입니다. 데이터 공급자가 지원하는 데이터 필드는 해당하는 GetSupportedDataFields 메소드를 통해 추가되므로 여기서는 그 추가에 대해 신경 쓰지 않습니다. 그러나 여기서 추가적인 정보가 필요합니다. 바로 앞서 무시했던 FieldProviders 구성원에 대한 것입니다. UIDataProviderField 데이터 필드 유형이 DATATYPE_Provider 가 _DATATYPE_ProviderCollection_으로 설정된 경우 FieldProviders 배열이 해당 데이터 필드에서 표현하는 데이터 공급자 인스턴스에 대한 참조를 포함해야 합니다. 데이터 유형이 _DATATYPE_Provider_(단일 데이터 공급자 표시)인 경우 배열은 하나의 요소만 포함합니다.

UCurrentGameDataStore::GetSupportedDataFields() 의 코드를 살펴보면 이상한 점을 확인할 수 있습니다. 즉 에디터에 있는지 여부에 따라 다른 로직을 갖게 됩니다. 자체 데이터 저장소를 만들 때는 이에 유의해야 합니다. 앞서 언급한 것처럼 필드 유형이 DATATYPE_Provider 또는 DATATYPE_ProviderCollection 인 데이터 필드를 추가할 때는 각 데이터 필드에 대한 FieldProviders 구성원에 대해 정확한 유형의 유효한 데이터 공급자 인스턴스에 대한 참조를 포함해야 합니다. 불행히도 CurrentGameDataStore 작동 방식 때문에 "player" 및 "team" 데이터 공급자는 게임에서 PlayerReplicationInfo 또는 TeamInfo 액터가 파생되는 경우에만 생성됩니다. 이러한 오브젝트가 에디터에서 파생되지 않으므로 UI 에디터 작업 시에는 FieldProviders 구성원에 넣을 공급자의 인스턴스가 없습니다. 그러면 어떻게 해야 할까요? 문제는 UCurrentGameDataStore::GetSupportedDataFields() 의 특수 코드가 어드레스하는 방법입니다. 에디터에서는 사실상 이러한 데이터 공급자로부터 어떤 데이터도 필요하지 않고 데이터 저장소 브라우저의 트리 보기를 입력할 수 있도록 이들이 지원하는 데이터의 목록만 필요합니다(또는 데이터 스키마). 이를 활용하여 실제 인스턴스 대신 해당하는 데이터 공급자 클래스의 클래스 기본 오브젝트를 사용함으로써 "DATATYPE_Provider/Collection에 데이터 공급자 인스턴스가 있어야 한다" 는 요구 사항을 만족할 수 있습니다. 이렇게 하면 데이터 브라우저 코드가 "Players" 및 "Teams" 집합에 해당하는 데이터 공급자를 쿼리하여 이러한 클래스의 인스턴스를 생성하지 않고도 에디터에서 지원되는 데이터 필드를 가져올 수 있습니다.

참고: 현재 UCurrentGameDataStore::GetSupportedDataFields()UCurrentGameDataStore::GetFieldValue() 는 게임 중에 "Players" 및 "Teams" 데이터 필드를 처리하지 않습니다(즉 GIsEditor == FALSE인 경우). 이것은 해당 함수의 버그이므로 이 예에서는 이를 건너뛰겠습니다.

4단계: 저장

다음 단계는 데이터 저장소 데이터 필드의 실제 값을 가져오는 위치를 결정하는 것입니다. 데이터 저장소 시스템을 처음 접할 때는 이 단계 역시 매우 헷갈립니다. 지원되는 필드 목록 생성을 위한 메소드처럼 데이터 저장소 값의 저장 및 검색에 사용하는 위치가 전적으로 임의적이라 말 그대로 모든 위치가 가능하기 때문입니다. 3단계에서는 몇 가지 기존 데이터 저장소가 지원되는 데이터 필드의 목록을 생성하는 것을 확인했습니다. 눈치채셨을 수도 있지만 각 데이터 저장소가 공개하는 데이터의 위치는 완전히 다릅니다. 자체 데이터 저장소를 생성할 때는 데이터를 어디든 저장할 수 있습니다. 그러나 데이터를 자체적으로 저장할지 또는 어딘가에 존재하는 데이터를 단순히 참조할지 여부를 결정할 때는 몇 가지 지침이 있습니다. 데이터를 구체 데이터 또는 추상 데이터 등의 두 종류 중 하나에 속한 것으로 간주하는 것이 유용합니다. 구체 데이터는 구분된 저장 위치가 있는 데이터입니다. 구체 데이터의 예로는 구성원 변수에 저장된 데이터를 들 수 있습니다. 추상 데이터는 구분된 저장 위치가 없는 데이터로, 절차적인 방법을 통해서만 데이터에 액세스할 수 있습니다. 추상 데이터의 예로는 메모리 내 모든 글꼴 오브젝트의 경로 이름 또는 현지화된 문자열의 값입니다. 현지화된 문자열이 구성 캐시에 있다 하더라도 실제 데이터는 디스크에 있는 것이므로 이를 추상으로 간주합니다. 특정 현지화된 값 또는 구성 값이 구성 캐시에 캐시된다는 보장이 없으므로 항상 액세서 메소드(및 액세서 메소드가 먼저 디스크로부터 데이터를 로드해야 할 수 있음)를 사용하여 데이터를 검색해야 합니다.

공개할 데이터가 추상 데이터인 경우 보통 값을 전혀 저장하지 않는 편이 낫습니다. 이 데이터가 절차적으로 생성되므로 보통은 데이터 값을 캐시하지 않습니다(예: 데이터 저장소 인스턴스의 구성원 변수에 저장). 데이터는 본질적으로 수신하는 순간에 이미 오래된 것일 수 있는 값을 포함하기 때문입니다. 따라서 추상 데이터에 대한 저장 위치 유형이 필요하지 않을 수도 있습니다. 구독자가 이러한 데이터 필드 중 하나의 값을 요청할 경우(GetDataStoreValue() 호출) 데이터를 생성하는 절차를 실행하여 구독자가 가장 최신 버전의 값을 갖도록 합니다. 이의 예로 필요 시 구성 캐시에서 현지화된 값 또는 구성 값을 검색하는 UUIConfigSectionProvider::GetFieldValue() 메소드를 살펴보겠습니다.

구체 데이터를 공개하는 데이터 저장소를 생성할 때는 이 데이터가 거의 항상 이미 어딘가에 저장되어 있으며 데이터 저장소/공급자가 이에 대한 액세스만을 제공합니다. 이 경우 데이터 상주 위치를 결정하기 전에 몇 가지 추가적인 고려 사항이 있습니다. 첫 번째 질문은 이 데이터가 게임에서 참조되는 방법과 범위를 결정하는 것입니다. 데이터가 코드 내 여러 지점에서 직접 참조될 경우 데이터를 그대로 두고 데이터 저장소도 기존 위치의 데이터를 참조하게 하는 것이 가장 좋은 방법이 될 것입니다. 데이터가 몇몇 위치에서만 참조되거나 액세서를 사용하여 데이터에 액세스할 경우 다른 고려 사항이 있습니다. 이 데이터 저장소의 구독자가 이 데이터를 어떻게 사용하는가입니다. 데이터가 주로 메뉴에서 사용되는지(예: 게임 또는 맵 설명) 또는 게임 중에 사용되는지 결정합니다(예: 게임 상태 데이터). 데이터가 주로 프런트 엔드 메뉴에서 사용되고 데이터의 현재 위치가 많은 컨텐트 참조를 포함하는 클래스 안에 있는 경우 데이터의 위치를 데이터 저장소로 옮기고 여기에서 검색하도록 이전의 기존 참조를 조정하는 것이 낫습니다. 이렇게 하면 뮤테이터 설명과 같은 사소한 데이터 부분만을 검색하기 위해 프런트 엔드 메뉴가 이 클래스를 로드하지 않아도 됩니다. 뮤테이터(또는 유사한 항목) 클래스도 런타임 시 데이터를 참조해야 할 경우 데이터 저장소 시스템에서 이 데이터를 간편하게 검색할 수 있으며 부작용도 없습니다.

확실히 데이터 저장소 클래스가 공개하는 구체 데이터가 아직 어딘가에 저장되지 않은 경우에는 구성원 값을 데이터 또는 공급자 값에 추가하여 데이터 값을 포함하도록 하는 것이 가장 간편할 것입니다.

5단계: 표현

표현은 데이터 저장소의 데이터 필드가 UI 에디터의 데이터 저장소 브라우저에 표시되는 방법을 말합니다. 데이터 저장소 브라우저는 데이터 저장소의 중첩 공급자 계층 구조를 표시하는 트리 목록이 있는 왼쪽 패널과, 선택한 데이터 저장소나 데이터 공급자에서 지원하는 데이터 필드를 표시하는 오른쪽 패널 등의 두 가지 패널을 포함합니다. DATATYPE_Provider 또는 DATATYPE_ProviderCollectionFieldType 을 갖는 데이터 필드는 오른쪽 패널에 표시되는 것과 함께 데이터 저장소의 하위 항목으로 표시됩니다. 다음 스크린샷을 참조하십시오.
  • 데이터 저장소의 데이터 필드 및 중첩 공급자
    datastore_toplevel.gif
  • 중첩 데이터 공급자의 데이터 필드
    datastore_nestedprovider.gif

데이터 저장소가 중첩 데이터 공급자를 포함할 때는 항상 중첩 데이터 공급자의 데이터 필드를 해당 데이터 공급자에 연결해야 합니다. 그러나 일부 경우 데이터 저장소의 데이터 필드인 것처럼 중첩 데이터 공급자의 데이터 필드를 표시하여 디자이너의 데이터 저장소 구현 세부 사항을 숨길 수 있습니다. 이를 데이터 공급자 축소라고 합니다. 많은 이유로 이 작업이 필요할 수 있지만 데이터 공급자 축소는 보통 추가적인 구분 레이어가 유용한 정보를 제공하지 않을 때 수행합니다. 예를 들어 단일 중첩 데이터 공급자가 있는 데이터 저장소를 생성하며(예: 런타임 시 유형 설정) 자체 데이터 필드가 없는 경우 데이터 저장소의 트리 항목이 유용한 정보를 포함하지 않으며 중첩 데이터 공급자 노드의 상위 트리 항목 역할만을 수행하게 됩니다. 데이터 저장소를 축소하고 등식에서 데이터 공급자 트리 항목을 제거하면 어느 것도 희생하지 않고 데이터 저장소를 위해 에디터 인터페이스를 간소화할 수 있습니다.

이제 데이터 공급자 축소가 무엇이며 축소가 필요한지를 알았습니다. 이제 그 방법을 살펴보겠습니다. 데이터 저장소의 GetSupportedDataFields() 메소드 호출 시 중첩 데이터 공급자에 대해 보통 DATATYPE_Provider의 FieldType 가 있는 데이터 필드를 추가하게 됩니다. 데이터 공급자를 축소하려면 데이터 공급자에 대한 데이터 필드를 추가하기 보다 데이터 공급자에서 직접 GetSupportedDataFields() 를 추가합니다. 그러면 모든 필드를 출력 배열에 필드로 추가하여 호출자가 해당 데이터 필드가 데이터 저장소 자체에 속한 것처럼 여기도록 합니다. 그러나 이렇게 하면 데이터 저장소 브라우저에서 데이터 저장소의 필드로 한 번, 그리고 중첩 데이터 공급자의 필드로 한 번 등, 두 번 필드가 표시되지는 않을까요? 사실은 그렇지 않습니다. 앞서 DATATYPE_Provider/Collection 데이터 필드 추가에서 언급한 것처럼 중첩 데이터 공급자에 대한 참조도 공급되어야 합니다. 그 이유 중 하나는 데이터 저장소 브라우저(및 GetSupportedDataFields() 를 호출하는 다른 코드)가 UIDataProviderField 구문의 FieldProviders 구성원을 사용하여 중첩 데이터 공급자가 지원하는 데이터 필드의 목록을 이끌어냅니다. 공급자에 대한 데이터 필드를 추가하지 않으므로(따라서 중첩 공급자에 대한 참조를 제공하지 않음) 데이터 저장소 브라우저가 중첩 데이터 공급자에서 GetSupportedDataFields() 를 호출하지 않습니다.

구현

1단계: 클래스 생성

구현의 첫 단계도 제목 그대로입니다. 데이터 저장소가 중첩 데이터 공급자를 사용할지 여부를 이미 결정했으므로 이 시점에서는 클래스를 생성하기만 하면 됩니다.

데이터 저장소에 대해 UIDataStore 에서 직접 파생할지 또는 특수 데이터 저장소 클래스 중 하나에서 파생하는 것이 나을지 결정해야 합니다. 각 데이터 저장소 클래스는 매우 특수하므로 보통은 자체 데이터 저장소 클래스 그룹에 대한 프레임워크를 구축하지 않는 한 UIDataStore 에서 직접 데이터를 파생하게 됩니다(데이터 저장소 프레임워크의 예는 UIDataStore_OnlineGameSearch 클래스 및 그 하위 참조).

데이터 공급자에는 다른 경우가 있습니다. 데이터 저장소 클래스에 매우 유용할 수 있는 기능을 포함하는 여러 데이터 공급자 클래스가 있으므로 자체적으로 작성하기 전에 이 모두에 익숙해지는 것이 나을 수 있습니다. 주요 데이터 공급자 클래스의 개요는 Data Provider Reference? 를 참고하십시오.

데이터 저장소 및 데이터 공급자 클래스를 만든 후에는 올바른 작동을 보장하기 위해 필요한 몇 가지 등록 정보가 있습니다.

UUIDataStore::Tag: 가장 먼저 구성해야 할 것은 데이터 저장소의 태그입니다. 데이터 저장소의 태그는 데이터 저장소 참조를 위해 데이터 저장소 마크업에서 사용할 수 있는 이름입니다. 데이터 저장소의 태그는 보통 변경이 필요하지 않습니다. 클래스의 defaultproperties 블록에서 Tag 값을 조정하기만 하면 됩니다. 데이터 저장소의 태그가 동적이거나 어떤 식으로 런타임 시 설정될 경우 UUIDataStore::GetDataStoreID() 메소드의 값을 덮어써서 데이터 저장소 식별에 사용되는 태그를 반환할 수 있습니다.

UUIDataProvider::WriteAccessType: 이 등록 정보는 데이터 저장소나 데이터 공급자의 데이터 필드를 런타임 시 수정 가능한지 여부를 결정하는 데 사용합니다. 기본값은 ACCESS_ReadOnly 로, 데이터 공급자의 어떤 필드도 변경을 수락하지 않음을 의미합니다. 데이터 공급자 또는 데이터 저장소 클래스 중에 데이터 저장소에 대한 데이터 게시를 지원하는 클래스가 있는 경우 이 값을 ACCESS_WriteAll 로 설정할 수 있습니다.

또한 파생해 올 데이터 공급자에 따라 데이터 공급자 클래스의 올바른 작동을 위해 필요한 추가적인 등록 정보가 있을 수 있습니다. 이러한 등록 정보는 Data Provider Reference? 에서 다룹니다.

2단계: 등록

데이터 저장소 클래스를 생성한 후에는 런타임 시 생성 및 초기화되도록 데이터 저장소 클라이언트에 등록해야 합니다. 앞서 언급한 것처럼 데이터 저장소 유형은 전역 데이터 저장소와 플레이어 데이터 저장소 등의 두 가지입니다. 등록 프로세스는 모두 유사합니다.

등록된 전역 데이터 저장소의 목록은 데이터 저장소 클라이언트의 GlobalDataStores 배열에 저장됩니다. 새로운 전역 데이터 저장소를 등록하려면 데이터 저장소 클래스의 경로 이름을 게임 DefaultEngine.ini의 [Engine.DataStoreClient] 섹션에 있는 GlobalDataStoreClasses 배열에 추가해야 합니다(사용자 지정 데이터 저장소 클라이언트를 사용할 경우 새 요소를 해당 클래스의 섹션에 추가). 예를 들어 ExampleGame에서 Fonts 데이터 저장소를 등록하려면 다음 항목을 ExampleGame/Config/DefaultEngine.ini 파일의 [Engine.DataStoreClient] 섹션에 추가해야 합니다. +GlobalDataStoreClasses="Engine.UIDataStore_Fonts".

등록된 플레이어 데이터 저장소 클래스의 목록은 데이터 저장소 클라이언트의 PlayerDataStoreClasses 배열에 저장됩니다. 새 플레이어 데이터 저장소 클래스를 등록하려면 데이터 저장소 클래스의 경로 이름을 게임의 DefaultEngine.ini에 있는 [Engine.DataStoreClient] 섹션에서 PlayerDataStoreClassNames 배열에 추가합니다. 전역 데이터 저장소 클래스가 등록되는 방식과 정확히 같습니다.

3단계: 메소드

이제 새 데이터 저장소 생성에서의 구체적인 부분을 시작합니다. 데이터 저장소와 공급자 클래스를 생성하고 데이터 저장소를 등록했으므로 데이터 저장소 작동에 필요한 메소드를 구현해야 합니다. 이 섹션에서는 이 메소드의 처음 반인 데이터 저장소 클래스에서 지원되는 데이터 필드와의 통신과 관련한 메소드를 다룹니다. 데이터 저장소 시스템을 설계할 때 목표 중 하나는 기본 클래스에서 가능한 많은 작업을 시도하여 하위 클래스가 데이터 저장소나 데이터 공급자의 전체 기능 중 일부분만을 구현하도록 하는 것입니다.

먼저 데이터 저장소 클래스에서 덮어써야 하는 필수적인 메소드를 살펴보겠습니다. 다음으로 매우 자주 덮어쓰게 되는 선택적인 메소드를 살펴봅니다. 마지막으로 거의 덮어쓰지 않는 선택적 메소드를 살펴봅니다. 둥근 괄호에 들어간 메소드 이름은 UnrealScript 메소드에 해당합니다.

필수 메소드
다음 메소드는 모든 데이터 저장소 및 데이터 공급자에서 구현되어야 합니다. 단 하나뿐입니다.

  • UUIDataProvider::GetSupportedDataFields() - 이 메소드는 데이터 공급자가 지원하는 데이터 필드를 결정하는 데 사용합니다. 데이터 필드가 없는 데이터 공급자는 있을 수 없으므로 모든 데이터 공급자 클래스는 이 메소드를 구현하게 됩니다. 데이터 저장소 브라우저에서 데이터 공급자가 특정 필드를 지원할지 여부를 검증할 때 다른 메소드와 함께 데이터 공급자가 지원하는 필드를 표시하는 컨트롤 입력을 위해 호출됩니다. 항상 Super::GetSupportedDataFields() 를 이 메소드 호출 GetSupportedScriptFields() 이벤트의 기본 구현으로 호출하여 비원시 데이터 공급자가 이 메소드 사용에 응답할 수 있는 기회를 제공합니다.

선택적 메소드(공통)
다음 메소드는 데이터 저장소나 데이터 공급자 클래스에서 덮어쓰거나 덮어쓰지 않아도 되는 메소드입니다. 먼저 일반적으로 덮어쓰는 메소드를 다루겠습니다.

  • UUIDataProvider::GetFieldValue() - 이 메소드는 데이터 필드에 대한 실제 데이터 값 검색에 사용합니다. 데이터 공급자가 자체 등록 정보 데이터 필드를 갖는 경우에만 이 메소드를 구현합니다. 즉 데이터 공급자나 데이터 저장소의 모든 데이터 필드가 중첩 데이터 공급자에 해당하는 경우 이 메소드가 필요하지 않습니다. 자체 데이터 저장소의 GetDataStoreValue() 메소드로 호출합니다.
  • UUIDataProvider::SetFieldValue() - 이 메소드는 데이터 필드에 대한 실제 데이터 위치에 업데이트된 값을 게시하는 데 사용합니다. GetFieldValue() 메소드처럼 이 메소드는 자체 등록 정보 데이터 값을 갖는 데이터 공급자에만 필요합니다. 자체 데이터 저장소의 SetDataStoreValue() 메소드로 호출합니다.
  • UUIDataProvider::GenerateFillerData() - 이 메소드는 UI 에디터에서의 미리 보기를 위해 가비지 데이터를 검색하는 데 사용됩니다. GetFieldValue() 메소드를 구현할 경우 이 메소드를 구현해야 합니다. 이 메소드는 현재 어디서도 호출되지 않지만 때때로 에디터에서 구독자가 호출합니다. 이 메소드 호출의 기본 구현은 GenerateFillerData() 이벤트를 호출하여 비원시 데이터 공급자가 이 메소드 사용에 응답할 수 있는 기회를 제공합니다.
  • UUIDataStore::InitializeDataStore() - 이 메소드는 모든 종류의 시작 또는 초기화 코드에 사용할 수 있습니다. 이 메소드를 구현해야 하는 상황은 매우 다양할 것입니다. 가장 일반적인 상황은 수명이 데이터 저장소의 수명과 연결된 데이터 공급자를 생성할 경우입니다(즉 데이터 저장소와 동시에 생성 및 파기됨). 이 메소드는 데이터 저장소 등록 시 데이터 저장소 클라이언트가 호출합니다.
  • UUIDataStore::OnRegister/OnUnregister (UIDataStore.Registered/UnRegistered) - 이 메소드 쌍은 초기화 수행 및 최종 정리 코드에 사용합니다. 데이터 저장소가 등록 및 등록 해제될 때 데이터 저장소에서 호출합니다. 이러한 해당 스크립트는 이러한 메소드에 액세스하는 데이터 저장소에만 스크립트를 제공합니다.
  • UUIDataStore::ResolveListElementProvider() - 이 메소드는 UIList의 요소를 제공할 수 있는 오브젝트에 대한 참조를 가져오는 데 사용합니다. 데이터 저장소나 중첩 데이터 공급자 중 하나가 집합인 데이터 필드를 갖는 경우 이 메소드를 구현해야 합니다. 반환된 오브젝트는 이 메소드의 매개 변수와 일치하는 FieldTag 가 있는 데이터 필드를 포함하는 데이터 공급자여야 합니다. 명백히 이 오브젝트는 UIListElementProvider 인터페이스도 구현해야 합니다. 이 메소드는 데이터 바인딩을 성공적으로 리졸브한 후 UIList에서 호출합니다.
  • (UIDataStore.NotifyGameSessionEnded) - 이 메소드는 레벨 전환에 앞서 발생해야 하는 정리를 수행하는 데 사용합니다. 데이터 저장소나 공급자가 액터에 대한 참조를 갖는 경우 이러한 참조로 인해 현재 세계가 레벨 전환 중 가비지 컬렉션되지 않을 수 있으므로 이 메소드를 구현해야 할 수 있습니다. 이 메소드는 자체 NotifyGameSessionEnded 메소드가 호출될 때 DataStoreClient에서 호출됩니다. 이 메소드는 =UWorld::CleanupWorld()==의 =GameViewport->eventGameSessionEnded() 에 대한 호출에서 생성된 메소드의 긴 체인 중 일부입니다.

선택적 메소드(드문 경우)
이러한 메소드는 덮어쓰는 경우가 거의 없습니다. 매우 특수한 상황에서만 이러한 메소드를 덮어씁니다.

  • UUIDataStore::GetDataStoreValue() - 이 메소드는 GetFieldValue() 메소드에 대한 최상위 래퍼입니다. 데이터 저장소가 이 메소드에 대한 호출을 받으면 마크업 문자열을 리졸브하여 마크업 문자열에서 참도하는 데이터 필드를 포함하는 데이터 공급자를 결정하고 적합한 데이터 공급자에 대해 GetFieldValue() 를 호출하여 값을 검색합니다. 이 메소드는 씬을 로드할 때, 위젯이 최초로 데이터 필드에 바인딩될 때, 그리고 구독자(위젯)가 표시할 값을 새로 고칠 때 *UIDataStoreSubscribers*(즉 위젯)에서 호출합니다. 이 메소드의 기본 구현은 대부분의 데이터 저장소에서 충분하므로 거의 덮어쓸 경우가 없습니다.
  • UUIDataStore::SetDataStoreValue() - 이 메소드는 SetFieldValue() 메소드에 대한 최상위 래퍼입니다. 데이터 저장소가 이 메소드에 대한 호출을 받으면 마크업 문자열을 리졸브하여 마크업 문자열에서 참도하는 데이터 필드를 포함하는 데이터 공급자를 결정하고 적합한 데이터 공급자에 대해 SetFieldValue() 를 호출하여 값을 게시합니다. 씬을 닫거나 게시자에서 SaveSubscriberValue() 가 호출될 때마다 UIDataStorePublishers 에서 호출합니다. 이 메소드의 기본 구현은 대부분의 데이터 저장소에서 충분하므로 거의 덮어쓸 경우가 없습니다.
  • UUIDataStore::GetProviderDataTag() - 이 메소드는 데이터 공급자에 대한 역방향 조회의 일종으로, 특정 데이터 공급자에 해당하는 데이터 필드 태그를 찾는 데 사용합니다. 이 메소드는 공급자의 데이터 저장소 경로를 생성하는 코드에서 사용됩니다(주로 데이터 저장소 브라우저에서만 사용). 이 메소드의 기본 구현은 대부분의 데이터 저장소에 충분하므로 데이터 저장소에 데이터 필드가 너무 많아 성능을 위해서 덮어쓰는 경우에만 조정이 필요할 것입니다.
  • UUIDataStore::ParseStringModifier() - 이 메소드는 지정한 태그를 기반으로 문자열에 인라인 스타일 수정을 적용하는 데 사용합니다. 글꼴 데이터 저장소처럼 문자열 렌더링을 수정하는 데이터 저장소에서만 사용됩니다(이를 통해 문자열 렌더링에 현재 사용하는 글꼴을 대체).
  • UUIDataStore::GetDefaultDataProvider() - 이 메소드는 데이터 공급자를 축소하는 데이터 저장소에서 사용합니다. 데이터 저장소 시스템의 많은 메소드는 입력 데이터에 대한 다양한 검증 루틴을 수행합니다. 그 예로는 데이터 공급자가 데이터 필드 태그를 지원하는지를 확인하는 것이 있습니다. 데이터 공급자 축소는 해당 데이터 공급자에 대한 참조를 포함하는 데이터 필드가 없음을 뜻하므로 이 메소드는 축소된 데이터 공급자가 있는 데이터 저장소에 대해 이러한 검증 루틴이 올바르게 작동하도록 하기 위한 백업으로 사용됩니다.
  • UUIDataStore::GetDataStoreID() - 이 메소드는 데이터 저장소를 고유하게 식별하는 데 사용되는 이름을 반환합니다. 기본 구현은 데이터 저장소의 태그를 반환하는 것이나(모든 데이터 저장소에 충분함) 이 메소드를 덮어써 현재 날짜와 같은 가변 데이터를 기반으로 생성된 태그를 반환할 수 있습니다.
  • UUIDataStore::OnCommit (UIDataStore.OnCommmit) - 이 메소드는 모든 구독자가 데이터 저장소에 대한 데이터 게시를 완료한 것을 데이터 저장소에 통지합니다. 데이터 저장소가 트랜잭션을 배치 처리하고 한 번에 모든 것을 게시할 경우에만 이 메소드를 구현합니다. 인터넷 게임 서버 등과 같이 원격 위치에 저장되는 데이터를 공개하는 데이터 저장소에서는 매우 일반적입니다.
  • (UIDataStore.SubscriberAttached/Detached) - 이 메소드는 데이터 저장소 구독자의 RefreshSubscriberValue() 메소드를 데이터 저장소의 RefreshSubscriberNotifies 목록에서 추가 또는 제거하는 데 사용합니다. 이 메소드는 데이터 저장소 바인딩을 성공적으로 리졸브할 때마다 구독자에서 호출합니다. 이 메소드의 기본 구현은 대부분의 데이터 저장소에서 충분하므로 거의 덮어쓸 경우가 없습니다.
  • (UIDataStore.RefreshSubscribers) - 이 메소드는 구독자에게 데이터 저장소 또는 중첩 데이터 공급자 중 한곳의 데이터 필드가 새 값으로 업데이트되었음을 알리기 위해 사용합니다. 이 메소드를 덮어쓰지 않아도 될 경우도 있지만 필드 중 하나가 업데이트되면 데이터 저장소나 데이터 공급자에서 이 메소드를 호출 해야 합니다.

테스트

이제 모든 필요한 메소드를 구현했으므로 데이터 저장소를 사용할 수 있습니다. 마지막 단계는 데이터 저장소의 올바른 작동을 확인하기 위한 테스트 씬을 설정하는 것입니다. 이 섹션에서는 씬 설정 방법과 UI 에디터에서의 데이터 조작 방법을 보여 줍니다.

1단계: 테스트 씬

맨 처음 확인할 것은 데이터 저장소 클래스가 사용 가능한 데이터 필드를 올바르게 공개하는지 여부입니다. 데이터 저장소 코드가 데이터 저장소 제공 데이터 필드 목록을 사용하여 데이터 저장소 바인딩을 검증하므로 데이터 저장소가 데이터 필드를 올바르게 제공하지 못하면 위젯을 해당 데이터 필드와 바인딩할 수 없습니다.

UnrealEd를 열고 새 UIScene을 생성합니다(이 방법을 확실히 모를 경우사용자 가이드? 참조). F7을 눌러 데이터 저장소 브라우저를 불러옵니다. 왼쪽 패널에 등록된 데이터 저장소 목록이 나타나며 이 목록에서 데이터 저장소의 태그를 확인할 수 있습니다. 데이터 저장소를 선택하면 지원되는 데이터 필드가 오른쪽 패널에 표시됩니다. 필드 이름이 올바르게 표시되며 모든 필드가 있고 유형이 모두 정확한지 확인합니다. 데이터 저장소에 중첩 데이터 공급자가 있는 경우 데이터 저장소의 태그 옆에 +가 표시되어 하위 항목이 있음을 나타냅니다. 항목을 확장하고 중첩 데이터 공급자 각각을 선택합니다. 각 항목의 이름이 정확하며 정확한 데이터 필드를 공급하는지 확인합니다. 데이터 저장소가 축소된 데이터 공급자를 사용할 경우 데이터 공급자가 데이터 저장소의 하위 항목으로 표시되지 않으며 데이터 저장소 자체 선택 시 데이터 공급자의 데이터 필드가 오른쪽 패널에 표시되는지 확인합니다.

다음으로 데이터 저장소가 데이터 값을 정확하게 공급하는지 확인하기 위해 필요한 위젯을 결정합니다. 몇 가지 다양한 데이터 필드 값 유형이 있으므로 필요한 위젯은 데이터 저장소가 지원하는 데이터 유형에 따라 달라집니다.

  • UILabel: 문자열, 텍스트, 수치 값 등으로 렌더링될 수 있는 단순 데이터
  • UIImage: 이미지 데이터
  • UIList: 배열 데이터
  • UISlider: 범위 데이터(0.0과 1.0 사이의 값을 갖는 부동소수점 등록 정보 등)

필요한 위젯을 결정한 후에는 씬에 해당 위젯을 배치합니다. 데이터를 오프라인으로 사용할 수 있는 경우(예: 맵 설명) 테스트해야 하는 각 데이터 필드 유형에 대해 단일 위젯만 필요합니다. 데이터가 실시간으로 업데이트될 때 에디터에서 바인딩을 전환할 수 있기 때문입니다. 게임 중에만 데이터를 사용할 수 있는 경우(예: 플레이어의 현재 점수) 같은 유형의 여러 위젯을 배치하여(테스트할 각 데이터 필드에 하나씩) 모든 데이터 필드를 한 번에 확인할 수 있게 합니다.

잘못된 경우
  • 데이터 저장소 브라우저를 연 다음 내 데이터 저장소가 왼쪽 패널의 트리 보기에 표시되지 않음: 구현 섹션 의 모든 단계를 수행했는지 확인합니다. 특히 데이터 저장소에 고유 태그를 부여했으며(데이터 저장소 클래스의 defaultproperties 블록에서 값 지정) 데이터 저장소를 올바르게 등록했는지 확인합니다.
  • 데이터 저장소 브라우저에서 내 데이터 저장소를 선택한 후 내 데이터 저장소의 지원되는 데이터 필드 태그가 오른쪽 패널에 표시되지 않습니다. GetSupportedDataFields() 를 구현했는지 확인합니다.
  • 내 데이터 저장소 클래스가 중첩 데이터 공급자를 가지나 브라우저에서 내 데이터 저장소의 트리 항목 아래 하위 항목이 없습니다. 해당 공급자에 대한 데이터 필드를 올바르게 추가하지 않았을 가능성이 있습니다. 데이터 저장소 클래스의 GetSupportedDataFields() 메소드를 재차 확인합니다. 표현 섹션 에서 언급한 것처럼 중첩 데이터 공급자가 데이터 저장소 브라우저에 표시되려면 DATATYPE_Provider 또는 DATATYPE_ProviderCollection 데이터 필드를 추가해야 합니다.

2단계: 위젯 바인딩

다음 단계는 데이터 저장소의 데이터 필드나 내부 데이터 공급자 중 하나에 위젯을 연결하는 것입니다. 이것을 데이터 필드에 대한 위젯 바인딩이라고 합니다. 위젯이 데이터 필드에 성공적으로 바인딩되면 씬을 열 때 데이터 필드의 값을 자동으로 로드하고 값을 표시하며 (데이터 공급자와 위젯이 모두 데이터 게시를 지원할 경우) 씬을 닫을 때 데이터 필드에 새 값을 다시 게시합니다. 또한 Kismet을 사용하여 언제든지 직접 표시 값을 새로 고치거나 게시할 수 있습니다(자세한 내용은 Kismet 섹션 참조).

데이터 저장소 바인딩에는 위젯 클래스에서의 데이터 저장소 바인딩 정의(설정)와 이 바인딩과 데이터 필드 연결(사용) 등의 두 가지 측면이 있습니다. 기본 위젯만을 사용하여 데이터 저장소를 테스트할 것이므로 여기서는 사용에 대해서만 다룹니다(설정은 다른 문서? 에서 다룸). Property 및 RangeProperty 데이터 필드의 바인딩 절차는 동일하나 Collection 및 ProviderCollection 데이터 필드의 바인딩 절차에는 몇 단계가 더 있습니다. 먼저 Property 데이터 필드와 UILabel의 바인딩을 다루고 ProviderCollection 데이터 필드와 UIList의 바인딩에 사용되는 추가적인 단계를 살펴보겠습니다.

등록 정보 데이터 필드 바인딩
먼저 바인딩할 데이터 필드를 선택합니다. 활성 UI 에디터 창에서 F7을 눌러 데이터 저장소 브라우저를 불러옵니다(아직 표시하지 않은 경우). 데이터 저장소 브라우저에는 'Strings' 와 'Other' 등의 두 탭이 있습니다. 'Strings' 탭은 게임의 INT 파일에 있는 현지화된 텍스트를 표시하고 'Other' 탭은 나머지 모든 것을 포함합니다. 'Other' 탭을 활성화한 다음 왼쪽 패널의 트리 보기에서 데이터 저장소를 선택합니다. 다음으로 오른쪽 패널에서 데이터 필드를 선택합니다(테스트할 데이터 필드가 중첩 데이터 공급자 안에 있는 경우 데이터 저장소의 트리 항목을 확장하고 데이터 공급자 하위 항목을 선택하여 오른쪽 패널에서 그 필드를 확인할 수 있습니다). UILabel을 사용하고 있으므로 "Property" 유형의 데이터 필드를 선택해야 합니다. 데이터 필드를 선택하고 다시 UI 에디터 창으로 전환하여 배치한 UILabel을 마우스 오른쪽 버튼으로 클릭합니다. 이제 유효한 데이터 필드를 데이터 저장소 브라우저에서 선택했으므로 단축 메뉴가 "Bind selected widgets to data field"(선택한 위젯을 데이터 필드에 바인딩) 옵션을 포함해야 합니다. 여기서 data field 는 데이터 저장소 브라우저에서 선택한 데이터 필드에 대한 데이터 저장소 경로입니다. 다음 스크린샷에서는(ExampleGame에서 저장) Display 데이터 저장소의 Gamma 등록 정보를 선택했습니다.
choose_binding.gif
단축 메뉴 옵션을 선택하여 위젯에 바인딩을 할당합니다. 그러면 위젯이 해당 등록 정보의 현재 값을 즉시 표시하기 시작합니다.

ProviderCollection 데이터 필드 바인딩
Collection이나 ProviderCollection 데이터 필드를 바인딩하는 작업은 위젯 바인딩 지정과 셀 바인딩 지정 등의 두 단계로 구성됩니다. 위젯 바인딩 할당은 Property 데이터 필드 바인딩과 동일하나 예외가 있습니다. "Bind" 단축 메뉴 옵션을 사용할 수 있도록 데이터 저장소 브라우저에서 Collection 또는 ProviderCollection 필드를 선택해야 합니다.
UIList를 배치한 다음 데이터 저장소 브라우저에서 GameResources 데이터 저장소로 이동하여 GameTypes ProviderCollection 필드를 UIList에 바인딩합니다. UIList는 다음과 유사할 것입니다.
bound_list.gif
기본적으로 UIList는 GameTypes ProviderCollection과 연결된 UIListElementProvider에서 제공하는 모든 열을 표시합니다. UIList의 너비에 따라 모든 열을 보거나 보지 못할 수 있습니다. "Description" 열이 보이지 않는다면 이 열이 보이도록 목록 크기를 재조정하십시오. 대부분의 경우 표시되는 순서나 표시할 열을 수정하고자 할 것입니다. 단축 메뉴나 목록 에디터 대화 상자 등의 두 가지 방법으로 조정할 수 있습니다.

단축 메뉴를 통한 열 편집
먼저 일부 셀을 제거합니다. 셀 제거 후 제거할 셀을 마우스 오른쪽 버튼으로 클릭합니다. 마우스를 "ClassName" 열에 놓고 오른쪽 버튼을 클릭하여 단축 메뉴를 불러옵니다. 단축 메뉴가 표시되면 "UIList" 옵션을 강조 표시하고 하위 메뉴에서 "Remove Cell" 을 선택합니다. bIsTeamGame, bIsDisabled 및 Description 필드에 대해서도 이 프로세스를 반복합니다. 이제 목록이 다음과 유사할 것입니다.
bound_list2.gif
이제 열 바인딩을 변경하겠습니다. "Map Prefix" 열을 마우스 오른쪽 버튼으로 클릭하고 UIList 메뉴 옵션을 아래쪽으로 스크롤하여 하위 메뉴를 확대합니다. 모든 지원되는 열의 이름이 단축 메뉴에 표시되는 것을 확인할 수 있습니다. "Map Prefix" 항목 옆에는 확인 표시가 있어 이 열이 현재 "Map Prefix" 필드에 할당되었음을 나타냅니다. "bIsTeamGame" 필드를 선택하여 bIsTeamGame 데이터 필드 값을 표시하도록 이 열의 바인딩을 변경합니다. 이제 이 열이 bIsTeamGame 등록 정보를 표시합니다. 대신 "Insert Column" 하위 메뉴에서 항목을 선택하여 열을 삽입할 수 있습니다. 새 열은 마우스를 오른쪽으로 클릭한 열의 바로 왼쪽에 삽입됩니다.
마지막으로 Description 필드를 다시 추가합니다. UIList를 마우스 오른쪽 버튼으로 클릭하고 UIList 메뉴 항목으로 스크롤합니다. 하위 메뉴에서 "Append Column" 을 강조 표시하여 표시되는 하위 메뉴에서 Description 항목을 선택합니다.
목록 에디터 대화 상자에서 열 편집
목록 에디터 대화 상자는 UIList 단축 메뉴 옵션보다 이전이며 원래는 목록 열을 수정할 수 있는 유일한 방법이었습니다. 목록 에디터 대화 상자에서 수행할 수 있는 모든 작업을 UIList 단축 메뉴에서 수행할 수 있지만 UIList의 단축 메뉴에 대한 직접 액세스를 제공하므로 여러 열을 수정할 때는 약간 더 빠를 수 있습니다. 목록 에디터 대화 상자를 열려면 UIList의 아무 곳이나 두 번 클릭합니다. 표시되는 대화 상자에서는 창 컨트롤을 사용하여 UIList의 표현을 확인할 수 있습니다. 열 제목의 마우스 오른쪽 버튼을 클릭하면 UI 에디터 창에서 UIList에 마우스를 올려 놓을 때 표시되는 것과 동일한 단축 메뉴를 불러올 수 있습니다. 행 제목줄을 마우스 오른쪽 버튼으로 클릭하면(숫자가 있는 열) 새 열을 첨부하거나 기존 열에 대한 열 바인딩을 변경할 수 있는 메뉴가 열립니다. 대화 상자의 나머지 부분(행, 공백)은 별 용도가 없습니다. OK를 클릭해야 변경 사항이 적용됩니다.
잘못된 경우
  • 단축 메뉴에 "Bind selected widgets to data field" 옵션이 표시되지 않음: 데이터 저장소 브라우저가 아직 초기화되지 않았습니다. "Data Store Browser" 옵션을 선택하거나 UI 에디터가 활성화된 상태에서 F7을 누릅니다.
  • "Bind selected widgets to data field" 옵션이 흐려짐: 먼저 테스트에 사용할 위젯에 하나 이상의 데이터 바인딩 등록 정보가 있으며 UIDataStoreSubscriber 인터페이스를 지원하는지 확인합니다. 이에 해당하지 않을 경우 데이터 저장소 브라우저에서 유효한 필드를 선택했는지 확인합니다. 마지막으로 선택한 데이터 필드가 바인딩하려는 위젯에 유효한지 확인합니다(즉 배열 값과 UILabel의 바인딩 시도).
  • "Bind selected widgets to data field" 를 선택하면 데이터 필드 값 대신 마크업 문자열이 표시됨: 단순한 설명은 데이터 저장소가 해당 이름을 갖는 데이터 필드를 포함하지 않는 경우입니다. 데이터 저장소 브라우저를 사용하여 위젯을 데이터 필드에 바인딩한 후에 발생할 경우 보통 데이터 필드가 플레이어 데이터 저장소에 속하는 경우일 것입니다. 에디터에 플레이어가 없으므로 사실상 게임을 실행하고 씬을 열어 이 바인딩을 테스트해야 합니다.
  • GameTypes 데이터 필드를 UIList에 바인딩할 때 항목이 표시되지 않음: GameTypes 데이터 필드의 요소는 DefaultGame.ini 파일에 정의된 Engine.UIGameInfoSummary PerObjectConfig 섹션을 사용하여 입력됩니다. 현재 ExampleGame만 UIGameInfoSummary 섹션을 포함하여 ExampleGame에서 테스트하지 않을 경우 ExampleGame에서 예시 항목을 복사해야 합니다. ExampleGame/Config/DefaultGame.ini 파일을 열고  UIGameInfoSummary=로 끝나는 모든 섹션(예: =[ExampleGameInfoSummary UIGameInfoSummary])을 자체 게임의 DefaultGame.ini 파일에 복사한 다음 에디터를 다시 시작합니다. 이제 예시 요소가 보일 것입니다.

데이터 저장소 시퀀스 오브젝트
다음 시퀀스 오브젝트는 데이터 저장소 바인딩 및 값 조작을 위해 제공됩니다.
  • Engine.UIDataStore_Images 데이터 저장소 바인딩 또는 값을 조작하는 대부분의 동작을 위한 추상 기준입니다.
  • Engine.UIAction_DataStoreField: 데이터 저장소 필드와의 작업에 사용하는 작업을 위한 추상 기준 클래스입니다.
  • Engine.UIAction_AddDataField (데이터 필드 추가): 새 데이터 필드를 씬 데이터 저장소에 추가합니다. 추가할 데이터 필드는 DataFieldMarkupString 등록 정보를 통해 지정됩니다("Markup String" 변수 링크, 예: <SceneData:IsUserLoggedIn>).
  • Engine.UIAction_GetDatafieldValue (데이터 저장소 값 가져오기): 마크업 문자열을 데이터 필드에 리졸브하고 이 값을 적합한 변수 링크에 복사한 다음 적합한 출력 링크를 활성화합니다. 각 데이터 필드 값 유형에 대한 출력 및 변수 링크를 포함합니다. 검색할 데이터 필드 값은 DataFieldMarkupString 등록 정보를 통해 지정됩니다("Markup String" 변수 링크).
  • Engine.UIAction_SetDatafieldValue (데이터 저장소 값 설정): 마크업 문자열을 데이터 필드에 리졸브하고 값을 데이터 필드에 게시합니다. 각 데이터 필드 값 유형에 대한 변수 링크를 포함합니다. 게시할 데이터 필드 값은 DataFieldMarkupString 등록 정보를 통해 지정됩니다("Markup String" 변수 링크). 게시할 값은 적합한 변수 링크를 통해 지정됩니다.
  • _Engine.UIAction_GetCellValue_(셀 값 가져오기): UIList의 단일 요소에 대한 특정 열 값에 액세스하는 데 사용할 수 있는 데이터 저장소 마크업 문자열을 생성합니다. 검색할 열은 CellFieldName 등록 정보를 통해 지정합니다. 요소는 CollectionIndex 등록 정보(인덱스 변수 링크)를 통해 지정합니다.
  • Engine.UIAction_PublishValue (값 저장): 대상 위젯이, 바인딩된 모든 데이터 필드에 값을 즉시 게시하도록 강제 적용합니다.
  • Engine.UIAction_RefreshBindingValue (값 새로 고침): 대상 위젯이, 바인딩된 모든 데이터 필드로부터 값을 즉시 새로 고치도록 강제 적용합니다.
  • Engine.UIAction_SetDatastoreBinding (데이터 저장소 바인딩 설정): 대상 위젯에 대한 데이터 저장소 바인딩을 변경합니다. 새 데이터 저장소 바인딩은 DataFieldMarkupString 등록 정보를 통해 지정됩니다("Markup String" 변수 링크).

사용 예
작성 예정

데이터 저장소 작업


구독자/게시자 구성

데이터 구독자/게시자 에서 언급한 것처럼 위젯이 데이터 저장소로부터 데이터를 검색할 수 있도록 지원하려면 UIDataStoreSubscriber 인터페이스를 구현해야 하며 데이터 저장소에 데이터를 게시하려면 UIDataStorePublisher 인터페이스를 구현해야 합니다(UIDataStorePublisher는 UIDataStoreSubscriber로부터 파생되므로 위젯은 이 인터페이스 중 하나를 구현해야 함). 다음 섹션에서는 데이터 저장소 구독자/게시자가 필요로 하는 메소드 및 등록 정보를 설명합니다.

메소드

  • UUIDataStoreSubscriber::SetDataStoreBinding() - 이 메소드는 이 구독자가 바인딩된 데이터 필드를 설정하는 데 사용됩니다. 이 메소드의 구현은 매우 간단합니다. 새 마크업 텍스트가 데이터 저장소 바인딩의 MarkupString 변수의 현재 값과 다를 경우 해당 값을 데이터 저장소 바인딩의 MarkupString 변수에 복사한 다음 RefreshSubscriberValue() 를 호출합니다. 이의 예는 UUILabel::SetDataStoreBinding() 을 참조하십시오.
  • UUIDataStoreSubscriber::GetDataStoreBinding() - 이 메소드는 마크업 텍스트 형식으로 현재 바인딩된 데이터 필드를 검색하는 데 사용됩니다. 이 메소드 구현도 간단합니다. 데이터 저장소 바인딩의 MarkupString 현재 값을 반환하면 됩니다. 이의 예는 UUILabel::GetDataStoreBinding() 을 참조하십시오.
  • UUIDataStoreSubscriber::RefreshSubscriberValue() - 이것은 구독자/게시자에 가장 중요한 메소드입니다. 이 메소드는 데이터 저장소 바인딩의 마크업 문자열을 실제 값에 리졸브하고 값을 위젯 자체에 적용하는 데 사용됩니다. 각 위젯의 구현은 약간씩 다릅니다. 예를 들어 대부분의 위젯은 마크업을 리졸브한 다음 데이터 저장소 바인딩의 적합한 값을 위젯 값에 적용합니다. 그러나 UIString은 마크업 텍스트를 직접 처리하기 때문에 UILabel은 단순히 호출을 기반 UIString에게 직접 전달합니다. 이 예는 UUILabel::RefreshSubscriberValue(), UUIImage::RefreshSubscriberValue()UUISlider::RefreshSubscriberValue() 를 참조하십시오.
  • UUIDataStoreSubscriber::GetBoundDataStores() - 이 메소드는 구독자가 현재 바인딩한 모든 UIDataStore에 대한 참조를 가져오는 데 사용합니다. 대부분의 구현은 매우 간단합니다. 데이터 저장소 바인딩을 출력 배열에 추가하기만 하면 됩니다(UUIImage::GetBoundDataStores() 참조). 위젯이 문자열 구성 요소를 사용할 경우(UIComp_DrawString) 데이터 저장소 참조가 UIString의 노드 배열에 포함되기 때문에 프로세스가 약간 다릅니다. 이 경우 호출을 구성 요소로 전달하기만 하면 참조가 모아집니다(UUILabel::GetBoundDataStores() 참조).
  • UUIDataStoreSubscriber::ClearBoundDataStores() - 이 메소드는 바인딩된 모든 데이터 저장소로부터 단일 구독자를 완전히 바인딩 해제하기 위해 사용합니다. 이 예는 UUIProgressBar::ClearBoundDataStores(), UUISlider::ClearBoundDataStores() 또는==UUIImage::ClearBoundDataStores()==를 참조하십시오.

  • UUIDataStorePublisher::SaveSubscriberValue() - 이 메소드는 위젯의 현재 값을 다시 바인딩된 데이터 필드에 복사하는 데 사용됩니다. 대부분의 경우 데이터 저장소 바인딩은 대부분의 작업을 처리하며 대부분의 이 메소드 구현에서는 호출을 데이터 바인딩의 SetBindingValue() 메소드에 전달하기만 하면 됩니다.

등록 정보

데이터 저장소 바인딩은 UIDataStoreSubscriber.UIDataStoreBinding 구문에서 처리합니다. 데이터 저장소 바인딩을 위젯 클래스에 추가하려면 UIDataStoreBinding 변수를 선언합니다. 이 변수는 앞서 언급한 메소드에서 작동하는 것입니다. 위젯이 한 데이터 유형만 지원할 경우 위젯 클래스의 defaultproperties 블록에서 데이터 바인딩의 RequiredFieldType 을 설정하여 이를 표시할 수 있습니다(예는 Engine.UIList 또는 Engine.UISlider 참조).

일반적으로 고려되는 UIDataStoreBinding 구문의 두 등록 정보는 RequiredFieldTypeMarkupString 변수입니다. RequiredFieldType 은 데이터 저장소 바인딩에 바인딩될 수 있는 데이터 필드 유형을 제한하는 데 사용되며 보통 위젯의 defaultproperties 블록에 할당됩니다. MarkupString 은 바인딩할 대상 데이터 필드를 찾는 데 사용되는 텍스트를 <DataStoreName:DataFieldTag> 형식으로 포함하며(자세한 내용은 이 섹션 참조) SetDataStoreBinding() 의 위젯에서 할당합니다. 이 구문의 나머지 등록 정보는 내부용입니다.

통지

이 섹션에서는 데이터 저장소 시스템에서 송수신하는 다양한 통지 유형에 대해 논의합니다.

데이터 필드 값 변경 통지

다음은 데이터 필드 값에 대한 변경 통지가 데이터 필드를 공개한 데이터 공급자로부터 데이터 공급자 체인을 통해 자체 데이터 저장소로 전파된 다음 데이터 저장소에 바인딩된 구독자로 향하게 하는 메소드를 설명합니다.

UIDataStore.RefreshSubscriberNotifies (및 해당하는 위임 함수 - OnRefreshDataStore)는 데이터 저장소의 값이 변경되었을 때 데이터 저장소 구독자에게 이를 통지하는 데 사용됩니다. 데이터 저장소 구독자가 데이터 저장소에 바인딩될 때 그 RefreshSubscriberValue 메소드가 UIDataStore.SubscriberAttached에 있는 데이터 저장소의 RefreshSubscriberNotifies 배열에 추가됩니다. 데이터 저장소가 값 중 하나가 업데이트된 것을 결정할 때는 RefreshSubscriberNotifies 배열을 반복하고 각 요소를 호출합니다. 구독자는 원하는 모든 항목에 이 콜백을 사용할 수 있습니다. 위젯의 경우 일반적인 응답은 데이터 저장소로부터 업데이트된 값을 표시하도록 값을 업데이트하는 것입니다. OnRefreshDataStore() 메소드는 변경된 데이터 필드의 태그를 포함하지만 지금은 이 값을 무시하도록 합니다.

UIDataProvider.ProviderChangedNotifies (및 해당하는 위임 함수 - OnDataProviderPropertyChange)는 이 데이터 공급자의 일부 등록 정보가 업데이트된 것을 자체 데이터 저장소에 통지하는 데 사용합니다. 데이터 저장소가 중첩 데이터 공급자 중 하나의 등록 정보가 업데이트된 것을 통지 받게 하려면 AddPropertyNotificationChangeRequest() 메소드를 호출하고 UIDataProvider.OnDataProviderPropertyChange 우임의 서명과 일치하는 함수로 전달하여 데이터 공급자 ProviderChangedNotifies 목록을 구독할 수 있습니다. 데이터 공급자가 값 중 하나가 업데이트된 것을 결정할 때는 이러한 통지를 발행합니다. 데이터 저장소가 이 함수 호출을 받을 때 일반적 응답은 아마도 RefreshSubscribers() 를 호출하여 모든 구독자에게 표시되는 값을 새로 고쳐야 할 수도 있음을 통지하는 것입니다. 이러한 통지는 변경된 데이터 필드의 태그를 포함하지만 지금은 이 값을 무시하도록 합니다.

데이터 값 변경 통지

다음은 게임플레이 오브젝트가 데이터 공급자가 공개하는 데이터 값에 대한 변경을 데이터 공급자에게통지하는 방법을 설명합니다.

때때로 메시지 발송 시스템에서 이를 처리하게 됩니다. 즉 데이터 저장소 시스템에 데이터 값 변경을 통지하는 것 외에도 통계 또는 메시지 브로드캐스트 시스템 등의 타 시스템에 이를 통지합니다. 이것은 아직 구현되지 않았으므로 하나 이상의 게임플레이 등록 정보 값에 대해 간단한(이상적인 것은 아니지만) 통지 시스템을 설정하는 방법에 대해 설명하겠습니다. 이러한 접근 방식에서는 databinding 키워드로 표시된 등록 정보 값이 변경될 때마다 위임을 사용하여 데이터 저장소 시스템에 통지하며 데이터 공급자 클래스가 UIDynamicDataProvider 에서 파생되었다고 가정합니다.

먼저 다음 위임을 Engine.Actor 에 추가합니다.

delegate OnDataValueChanged( optional name PropertyThatChanged );

데이터 공급자 클래스의 ProviderInstanceBound() 이벤트에서 인스턴스의 OnDataValueChanged 위임을 데이터 공급자의 NotifyPropertyChanged() 이벤트에 할당합니다. 데이터 공급자의 ProviderInstanceUnbound() 이벤트에서 인스턴스의 OnDataValueChanged 위임을 None으로 설정합니다. 그러면 데이터 공급자로부터의 통지를 위쪽으로 자체 데이터 저장소와 데이터 저장소 구독자에게 전파할 수 있도록 합니다. 이제 액터 클래스(즉 데이터바인딩 변수를 포함하는 클래스)를 통해 첫 번째로 통지를 보내게 해야 합니다.

다음으로 databinding 키워드가 표시된 등록 정보 값이 변경될 때 이를 탐지하고 데이터 저장소 시스템에 통지를 보낼 수 있는지 확인하고자 합니다. databinding 키워드가 있는 등록 정보에 대한 액세스를 제한하면 가장 간편하게 수행할 수 있습니다. 이를 직접 수행하거나(비공개 키워드 추가) 스크립트 컴파일러를 수정하여 databinding 키워드로 선언된 변수를 자동으로 비공개로 지정하면 됩니다. 스크립트 컴파일러가 이를 수행하도록 하려면 "else if ( Specifier.Matches(NAME_DataBinding) )" check: 안에 있는 FScriptCompiler.GetVarType()= 블록에 다음 행을 추가합니다.

// make this var private in unrealscript
ObjectFlags &= ~RF_Public;
ObjectFlags &= ~RF_Protected;

// make this var private in C++
ExportFlags |= PROPEXPORT_Private;
ExportFlags &= ~(PROPEXPORT_Public|PROPEXPORT_Protected);

다음으로 데이터 바인딩 변수에 대한 액세서를 추가하여(값 변경 외에도) 액터의 OnDataValueChanged 위임을 호출함으로써 데이터 저장소 시스템에 등록 정보 변경을 알리려고 합니다. 데이터 저장소 시스템이 복제를 통해 데이터가 변경될 때 통지를 받을 수 있도록 eventReplicatedEvent()==를 호출한 직후에 =UActorChannel::ReceivedBunch()delegateOnDataValueChanged 에 대한 호출을 삽입하는 것도 좋은 방법이 될 수 있습니다. 기본적으로 이를 수행하면(액터의 ReplicatedEvent 스크립트 대신) 사실상 아무 것도 할당되지 않았을 때 위임을 호출하는 것을 =IS_DELEGATE_SET 매크로를 활용하여 방지할 수 있습니다.

실행 흐름

이 섹션에서는 데이터 저장소 시스템 작업과 관련한 다양한 활동의 실행 흐름을 단계별로 설명합니다.

값 검색

다음 단계는 씬을 최초로 열 때 씬 데이터 저장소 바인딩에 대한 값을 리졸브 및 로드할 때 고려됩니다.
  1. 유효한 데이터 저장소 바인딩이 있는 위젯을 포함하는 씬은 UGameUISceneClient::OpenScene() 을 통해 엽니다.
  2. UUIScene::LoadSceneDataValues()UUIScene::Activate() 에서 호출합니다. 이것은 UIDataStoreSubscriber 인터페이스를 구현하는 하위의 목록을 구성하는 씬의 모든 하위를 반복합니다.
  3. UUIScene::LoadSceneDataValues() 는 각 구독자에 대해 RefreshSubscriberValue() 를 호출합니다.
  4. RefreshSubscriberValue()UIDataStoreBinding 등록 정보 각각에 대해 ResolveMarkup() 을 호출합니다(현재 모든 구독자에는 단 하나의 데이터 저장소 바인딩이 있음).
  5. FUIDataStoreBinding::ResolveMarkup() 은 마크업 문자열을 파싱 및 리졸브하여 정확한 데이터 저장소에 대한 참조를 가져옵니다.
  6. 데이터 저장소에 대한 참조를 수립한 후에는 데이터 저장소의 SubscriberAttached() 이벤트를 호출하여 위젯이 구독자 콜백의 데이터 저장소 배열에 추가됩니다(UUIDataStore::RefreshSubscriberNotifies).
  7. RefreshSubscriberValue() 메소드로 돌아가 위젯이 데이터 저장소 바인딩에 대한 GetBindingValue 를 호출하여 마크업 문자열이 참조하는 데이터 필드의 값을 검색합니다.
  8. 위젯이 데이터 저장소로부터 검색한 값을 표시하기 시작합니다.

다음 단계는 Engine.UIAction_RefreshBindingValue 클래스를 사용하여 위젯의 표시 값을 직접 새로 고치기 위한 것입니다.

다음 단계는 Engine.UIAction_GetDatafieldValue 클래스를 사용하여 임의의 데이터 필드 값을 검색하기 위한 것입니다.

다음 단계는 Engine.UIAction_GetCellValue 클래스를 사용하여 목록 요소의 특정 셀에 대한 값을 검색하기 위한 것입니다.

값 게시

다음 단계는 씬이 닫혔을 때 데이터 저장소에 값을 게시하기 위한 것입니다.
  1. UUIScene::SaveSceneDataValues()UUIScene::Deactivate() 에서 호출합니다. 이것은 UIDataStoreSubscriberUIDataStorePublisher 인터페이스를 구현하는 하위 위젯의 두 개별 목록을 구성하는 모든 하위를 반복합니다.
  2. UUIScene::SaveSceneDataValues() 는 모든 구독자에 대해 ==SaveSubscriberValue()==를 호출합니다.
  3. SaveSubscriberValue() 는 데이터 저장소에 게시될 값을 포함하는 UIProviderScriptFieldValue, 위젯에 바인딩될 데이터 저장소 목록을 검색하는 GetBoundDataStores() 를 생성한 다음 UIDataStoreBinding 등록 정보 각각에 대해 SetBindingValue() 를 호출합니다.
  4. FUIDataStoreBinding::SetBindingValue() 에서 바인딩에 유효한 데이터 저장소 참조가 있는 경우 데이터 필드 태그와 새 값을 전달하는 SetDataStoreValue() 를 호출합니다.
  5. UUIDataProvider::SetDataStoreValue()ParseDataStoreReference() 를 호출하여 마크업 문자열을 파싱하고 마크업 문자열에서 참조하는 데이터 필드를 소유하는 데이터 공급자를 찾습니다.
  6. UUIDataProvider::SetDataStorevalue() 가 데이터 필드를 소유하는 데이터 공급자를 제대로 찾은 경우 위젯의 데이터 저장소 바인딩 등록 정보로부터 수신한 새 값에서 해당 데이터 공급자 전달에 대한 SetFieldValue() 를 호출합니다.
  7. 데이터 공급자의 SetFieldValue() 메소드가 적합한 위치에 값을 게시합니다.
  8. SaveSceneDataValues() 로 돌아가 UIDataStorePublisher 인터페이스를 구현하는 모든 하위 위젯에서 SaveSubscriberValue() 가 호출된 후에는 각 데이터 저장소에 대해 OnCommit() 을 호출하여 모든 위젯이 데이터 게시를 완료했음을 표시합니다.
  9. 마지막으로 모든 구독자 위젯에 대해 ClearBoundDataStores() 를 호출합니다. 이를 통해 각 위젯과, 위젯이 바인딩된 데이터 저장소 간의 바인딩을 제거합니다.
  10. ClearBoundDataStores() 에서 데이터 저장소의 SubscriberDetached() 이벤트를 호출하여 구독자 콜백의 데이터 저장소 배열로부터 위젯을 제거합니다.

다음 단계는 Engine.UIAction_PublishValue 클래스를 사용하여 위젯의 현재 값을 직접 게시하기 위한 것입니다.

다음 단계는 Engine.UIAction_SetDatafieldValue 클래스를 사용하여 임의의 데이터 필드에 값을 게시하기 위한 것입니다.

다음 단계는 Engine.UIAction_AddDataField 클래스를 사용하여 가변 데이터 저장소에 새로운 데이터 필드를 추가하기 위한 것입니다.

데이터 저장소 예


다음 섹션은 데이터 저장소의 다양한 유형을 생성하기 위한 단계입니다. 여기서는 "데이터 저장소 생성" 섹션 에서 나열한 단계에 따라 고유의 데이터 저장소 생성에 각 단계를 적용하는 방법을 설명하겠습니다.

"레지스트리" 데이터 저장소

레지스트리 데이터 저장소의 용도는 모든 씬에서 전역으로 액세스할 수 있는 데이터 저장을 위한 단일 위치를 제공하는 것입니다. 이 예는 2007년 3월 QA 릴리스 현재 코드베이스에 통합되었으며 작동을 위해서는 2007년 3월 QA 릴리스가 필요합니다.

설계

  • 데이터 유형: 레지스트리 데이터 저장소에 저장하려는 데이터는 알 수 없으며 디자이너가 런타임 시 데이터 필드를 추가하지만 여기서는 간단한 데이터 유형만 지원이 필요하므로(범위나 배열 데이터 없음) DATATYPE_Property만 지원하면 됩니다.
  • 구성 요소: 디자이너가 런타임 시 자체 데이터 필드를 추가할 수 있도록 할 것이므로 Engine.UIDynamicFieldProvider 클래스를 내부 데이터 공급자로 사용해야 합니다. UIDynamicFieldProvider 클래스는 사용자가 추가한 필드를 기준으로 모든 지원되는 필드의 목록을 동적으로 생성하는 프레임워크 데이터 공급자입니다. 이것은 수행이 필요한 모든 레지스트리 데이터 저장소에 대한 것이므로 레지스트리 데이터 저장소는 복합 데이터 저장소입니다.
  • 데이터 필드 목록 생성: 이 데이터 저장소의 모든 데이터 필드는 사용자가 추가했으므로 이 단계는 레지스트리 데이터 저장소에 적용되지 않습니다.
  • 저장: 다시 말하지만 데이터 필드는 사용자가 추가한 것이므로 이 단계에서는 이에 대해 고려할 것이 없습니다. 데이터는 데이터 공급자의 RuntimeDataFields 배열에 저장됩니다.
  • 표현: 모든 기능은 중첩 UIDynamicFieldProvider에서 제공되므로 데이터 공급자를 축소하여 데이터 저장소 자체의 필드인 것처럼 필드를 표시함으로써 이 데이터 저장소 작업을 간소화할 수 있습니다.

구현

클래스 제작
먼저 클래스를 생성해야 합니다. 이 특정 데이터 저장소는 다른 것으로부터 기능을 상속 받지 않으므로 UIDataStore에서 직접 파생할 것입니다. 이 예를 위해 계속 Engine 패키지를 활용할 것입니다. 또한 여러 원시 함수를 구현해야 하므로 기본 상태여야 합니다. 먼저 클래스의 defaultproperties 블록에서 Tag 및 WriteAccessType 등록 정보에 값을 할당해야 합니다. 태그는 임의적이나 레지스트리 데이터 저장소를 만들고 있으므로 이를 'Registry' 라고 하겠습니다. 레지스트리 데이터 저장소의 목적은 디자이너가 임의의 데이터 값을 저장할 수 있도록 하는 것입니다. 데이터 저장소에 대한 게시를 허용하려면 WriteAccessType을 ACCESS_WriteAll로 설정해야 합니다. 그런 다음 중첩 데이터 공급자를 사용하고 있으므로 UIDynamicFieldProvider 변수 선언을 중첩 데이터 공급자에 대한 참조를 갖도록 추가해야 합니다. 마지막으로(잊기 전에) cpp 파일(UnUIDataStores.cpp 사용)에 등록 행을 추가하여 클래스 및 기본 오브젝트가 시작 시 생성되게 합니다. 계속하여 unrealscript(머리말 내보내기를 물으면 Yes 선택)를 컴파일하고 C++을 컴파일합니다. 지금까지의 클래스는 아래와 유사할 것입니다.

(UIDataStore_Registry.uc)

/**
 * 디자이너가 모든 씬에서 전역으로 액세스하는 임의의 데이터 필드를 추가. 제거 및 수정할 수 있게 함
 */
class UIDataStore_Registry extends UIDataStore
   native(inherit);

/**
 * 이 데이터 저장소에 추가된 데이터 필드를 포함하는 데이터 공급자
 */
var   protected   UIDynamicFieldProvider      RegistryDataProvider;

DefaultProperties
{
   Tag=Registry
   WriteAccessType=ACCESS_WriteAll
}


(anywhere in UnUIDataStores.cpp)
IMPLEMENT_CLASS(UUIDataStore_Registry);

등록
다음으로 시작 시 생성되어 사용할 수 있도록 데이터 저장소 클라이언트에 새 데이터 저장소를 등록해야 합니다. 이 데이터 저장소는 특정 플레이어와 연결되지 않으므로(각 플레이어마다 필요 시 별도의 레지스트리를 갖게 할 수는 있음) 전역 데이터 저장소입니다. 게임의 DefaultEngine.ini를 열고 [Engine.DataStoreClient] 섹션을 찾습니다. 없는 경우 추가합니다. 다음 행을 [Engine.DataStoreClient] 섹션에 추가합니다. +GlobalDataStoreClasses="Engine.UIDataStore_Registry". 이 행은 데이터 저장소 클라이언트가 데이터 저장소 클라이언트를 초기화할 때 레지스트리 데이터 저장소를 생성하도록 지시합니다.

메소드

GetSupportedDataFields() 메소드는 모든 데이터 저장소에서 구현해야 하므로 이 메소드를 구현해야 함은 알고 있습니다. 이것부터 시작하겠습니다. cpptext 블록을 UIDataStore_Registry.uc 파일에 추가하고 대부분 다른 데이터 저장소드로부터 해당 메소드의 선언을 복사합니다(UIDataStore_Fonts는 좋은 예임). 모든 데이터 필드는 중첩 데이터 공급자에 저장되므로 자체 필드는 추가하지 않습니다. 또한 데이터 공급자가 축소 표시되기를 바라므로 데이터 공급자 자체에 대한 데이터 필드를 추가하기 보다는 호출을 직접 UIDynamicDataProvider에 전달할 것입니다. 이 구현은 다음과 유사할 것입니다.
/**
 * 이 데이터 공급자가 공개한 데이터 필드 목록 가져오기
 *
 * @param   out_Fields   이 데이터 공급자의 데이터 액세스에 사용할 수 있는 태그 목록으로 채워짐
 *                  GetScriptDataTags를 호출하여 스크립트 전용 하위 클래스가 이 목록에 추가될 수 있게 함
 */
void UUIDataStore_Registry::GetSupportedDataFields( TArray<FUIDataProviderField>& out_Fields )
{
   // data stores empty the array, data providers append to the array
   out_Fields.Empty();

   // 데이터 공급자를 축소하기 위해 DATATYPE_Provider 요소를 out_Fields 배열에 추가하는 대신 직접 호출을 전달함
   if ( RegistryDataProvider != NULL )
   {
      RegistryDataProvider->GetSupportedDataFields(out_Fields);
   }

   // 스크립트 전용 하위 클래스가 필드 목록에 추가될 수 있게 함
   Super::GetSupportedDataFields(out_Fields);
}

축소된 데이터 공급자를 사용하고 있으므로 데이터 필드 확인이 축소된 공급자에 전달되도록 GetDefaultDataProvider() 를 구현해야 합니다. 이 구현은 다음과 유사할 것입니다.

/**
 * 이 데이터 공급자의 태그를 제공하는 데이터 공급자에 포인터 반환. 일반적으로 이 데이터 공급자이나
 * 이 데이터 저장소에서는 데이터 필드를 내부 공급자로부터 가져와 데이터 저장소 자체의 필드인 것처럼 표시함
 */
UUIDataProvider* UUIDataStore_Registry::GetDefaultDataProvider()
{
   if ( RegistryDataProvider != NULL )
   {
      return RegistryDataProvider;
   }

   return this;
}

다음으로 데이터 검색 및/또는 게시 메소드를 구현합니다. 이 데이터 저장소에서는 데이터에 대한 읽기 및 쓰기 액세스를 허용하므로 GetFieldValue() 및==SetFieldValue()== 메소드를 구현합니다. 축소된 데이터 공급자를 사용하고 있으므로 이 메소드의 구현이 매우 간단합니다. 즉 호출을 중첩 데이터 공급자에 직접 전달합니다.

/**
 * 지정한 데이터 필드의 값을 리졸브하여 출력 매개 변수에 저장합니다.
 *
 * @param   FieldName      값을 리졸브할 데이터 필드, 이 공급자가 값을 리졸브할 수 있는
 *                     등록 정보에 해당하도록 확인(즉 내부 공급자에 해당하는 태그 등이 아님)
 * @param   out_FieldValue   지정한 등록 정보에 대해 리졸브된 값 수신
 *                     @추가 참고는 ParseDataStoreReference 참조
 * @param   ArrayIndex      데이터 집합에 사용할 선택적 배열 인덱스
 */
UBOOL UUIDataStore_Registry::GetFieldValue( const FString& FieldName, FUIProviderFieldValue& out_FieldValue, INT ArrayIndex/*=INDEX_NONE*/ )
{
   UBOOL bResult = FALSE;

   if ( RegistryDataProvider != NULL )
   {
      bResult = RegistryDataProvider->GetFieldValue(FieldName, out_FieldValue, ArrayIndex);
   }

   return bResult;
}

/**
 * 지정한 데이터 필드 값을 리졸브하고 해당 필드의 적합한 위치에 명시된 값을 저장합니다.
 *
 * @param   FieldName      값을 리졸브할 데이터 필드, 이 공급자가 값을 리졸브할 수 있는
 *                     등록 정보에 해당하도록 확인(즉 내부 공급자에 해당하는 태그 등이 아님)
 * @param   FieldValue      지정한 등록 정보 저장을 위한 값
 * @param   ArrayIndex      데이터 집합에 사용할 선택적 배열 인덱스
 */
UBOOL UUIDataStore_Registry::SetFieldValue( const FString& FieldName, const FUIProviderScriptFieldValue& FieldValue, INT ArrayIndex/*=INDEX_NONE*/ )
{
   UBOOL bResult = FALSE;

   if ( RegistryDataProvider != NULL )
   {
      bResult = RegistryDataProvider->SetFieldValue(FieldName, FieldValue, ArrayIndex);
   }

   return bResult;
}

이제 모든 것이 거의 제대로 갖추어진 것 같습니다. 이제 두 단계만 남아 있습니다. 데이터 저장소가 초기화될 때 중첩 데이터 공급자를 생성하고 적합한 시점에 데이터를 저장하도록 지시해야 합니다. UIDynamicFieldProvider 클래스는 PerObjectConfig이므로(QA 3월 릴리스까지 PerObjectConfig로 표시되지 않음) 이전에 저장한 값을 올바르게 로드할 수 있도록 매번 같은 이름을 사용하여 데이터 공급자가 생성되도록 확인해야 합니다.


/**
 * 이 레지스트리 데이터 저장소에 대한 데이터 공급자 생성
 */
void UUIDataStore_Registry::InitializeDataStore()
{
   Super::InitializeDataStore();

   if ( RegistryDataProvider == NULL )
   {
      // UIDynamicFieldProvider가 PerObjectConfig이므로 ConstructObject에 대한 호출에서 이름을 제공하여 이전에 이 데이터 저장소가
      // .ini 에 저장한 데이터가 올바르게 로드되도록 해야 함
      RegistryDataProvider = ConstructObject<UUIDynamicFieldProvider>(UUIDynamicFieldProvider::StaticClass(), this, TEXT("UIRegistryDataProvider"));
   }

   check(RegistryDataProvider);

   // 이제 데이터 공급자에게 이전에 저장한 필드가 .ini로부터 로드한 데이터 필드의 런타인 배열을 입력하도록 알림
   RegistryDataProvider->InitializeRuntimeFields();
}

/**
 * 현재 씬에서 이 데이터 저장소에 바인딩된 모든 값이 저장되었음을 데이터 저장소에 통지. UI 시스템이 데이터 저장소에
 *  대한 데이터 쓰기를 마친 시점을 결정하는 방식으로 버퍼링 또는 배치 데이터 트랜잭션을 수행하는 데이터 저장소 제공
 */
void UUIDataStore_Registry::OnCommit()
{
   Super::OnCommit();

   // UI 시스템(또는 이 데이터 저장소를 사용하는 주체)이 데이터 값 게시를 완료합니다. 모든 것을 영구 스토리지로 전송할 시점
   if ( RegistryDataProvider != NULL )
   {
      // RegistryDataProvider는 .ini에 데이터를 저장하므로 영구 스토리지에 이 공급자에 대한 데이터를 보내면 SaveConfig()를 호출해야 함
      RegistryDataProvider->SavePersistentProviderData();
   }
}

데이터 저장소 구성

작성 예정

온라인 데이터 저장소

목록이 데이터 저장소로부터 데이터를 가져오는 이유와 방식(즉 몇 가지 간접 레이어를 통해)을 명확히 하려면 UIListsKR 페이지를 참조하십시오.

데이터 저장소 시스템은 특수 형식의 마크업 문자열을 사용하여 동일한 데이터를 간편히 검색하는 방식을 제공합니다.

<TheDataStore:PrimaryField;DataStoreIndex.InternalField>

예를 들어 목록이 OnlineGameSearch 데이터 저장소의 SearchResults라는 필드에 바인딩될 경우(현재 선택한 게임 유형에 대해 GameSearchCfg 구문의 UIDataProvider_Settings 오브젝트의 배열에 효과적으로 매핑) 다음과 유사한 마크업 문자열을 사용하여 현재 선택된 서버에 대한 설정 데이터 공급자의 필드에 액세스할 수 있습니다.

local string markup;
markup = "<OnlineGameSearch:SearchResults;" $ MyList.GetCurrentItem() $ ".NumPublicConnections>";

class'UIRoot'.static.GetDataStoreFieldValue(markup, FieldValue, GetScene(), GetPlayerOwner());

또는 (덜 강력함 - 목록이 바인딩된 데이터 저장소 정보 가정):

// assuming GetCurrentItem() returned '4'
MyList->DataSource.ResolvedDataStore->GetDataStoreValue("SearchResults;4.NumPublicConnections", FieldValue);

작성 예정: 일치 검색, 호스트에 연결된 친구 초대, 관련 플레이어 표시 등

플레이어 정보 데이터 저장소

HUD에 정보를 표시할 때는 데이터 저장소 시스템을 사용하지 않는 편이 더 간편할 수 있습니다. 정말로 필요한 것은 문제의 위젯에 대한 참조를 가져온 다음(씬에서 FindChild 호출) 이에 대해 SetValue()를 호출하는 것입니다. 예를 들어 UILabel의 경우 SetValue()가 값의 문자열 표현을 전달합니다.

다음은 데이터 저장소 시스템을 통해 데이터를 공개 및 참조하는 방법에 대한 설명입니다.

참조하는 데이터가 PlayerReplicationInfo에 저장되었다고 가정하고, 등록 정보가 databinding 키워드로 표시된 후에는 위젯을 플레이어 데이터 공급자와 바인딩함으로써 게임 내 값에 액세스할 수 있게 됩니다. 이를 수행하는 방식은 게임 내 모든 플레이어에 대해 이 정보를 표시해야 할지 또는 자체 플레이어에 대해서만 표시할지 여부에 따라 달라집니다.

모든 플레이어에 대해 마크업 문자열은 다음과 같습니다.

<CurrentGame:Players;##.MyValue>

여기서 ##은 관련 플레이어의 GRI PRIArray에 대한 인덱스입니다.

플레이어 소유자에 대해 마크업 문자열은 다음과 같습니다.

<PlayerOwner:MyValue>

SetDataStoreBinding()을 호출하고 위의 마크업 문자열에 전달하여 레이블에 마크업 문자열을 할당합니다. 그러면 값이 레이블에 바인딩되고 레이블이 자동으로 현재 값을 표시하기 시작합니다. 레이블의 텍스트를 새로 고치려면(예: 표시하는 값이 오래된 경우) 레이블에서 RefreshSubscriberValue()를 호출합니다.

바인딩된 대상 값이 수정되어 표시된 값 자체를 새로 고칠 때 레이블에게 이를 통지하는 방법이 있으나 이것이 더 관련이 높습니다.

관련 항목


작업 중 - 좀 더 확장하고자 하는 항목 목록만 소개합니다.
How to implement support for data binding in a custom widget class? - 관련 메소드 및 인터페이스, 데이터 저장소에 바인딩된 위젯에 대한 시작부터 끝까지의 전체 이벤트 체인