🎮Unreal4/C++_Plugin

[UE4] Detail View 커스텀 : 체크박스 (DesiredWidth, StandardDialog, UniformGridPanel, IPropertyHandle, AddProperties)

공대 컴린이 2023. 5. 8. 21:03
728x90

이전 구현 결과

이전 학습에는 Property의 내용을 접었다 펼 수 있는 커스텀 Property를 대략적으로 만들었다.

 

구현 목표 결과

이번에는 커스텀 Property에 Equipment의 데이터를 올바르게 출력하고, 체크박스를 추가하는 방법에 대해 학습해보았다.

☑️ Customize Header에 CheckBox 그리기

먼저, 커스터마이징 Equipment Property를 구성하는 SEquipmentData 클래스의 CustomizeHeader 함수에서 헤더를 정의하였다.

 

헤더의 ValueContent 영역의 넓이를 조절하기 위해 MinDesiredWidth MaxDesireWidth 명령어를 사용하여 넓이의 최솟값과 최댓값을 설정해주었다.

 

Engine → Source → Runtime → SlateCore → Private → Styling → CoreStyle.cpp

DesireWidth 를 설정하는 Style값엔진의 StandardDialog를 사용하였다.

 

이후 헤더에는 Equipment 데이터 항목들을 체크박스로 만들기위해 CheckBoxes 객체를 만들고, Draw 함수를 호출하였다.

☑️ Check Box 클래스 생성

Check Box 클래스에서는 내부적으로만 사용할 FInternalData 구조체체크박스로써 항목을 추가시킬 함수들을 작성하였다.

 

FInternalData 구조체는 체크박스가 체크 되었는지를 확인할 bChecked 변수와 PropertyHandle과, 체크박스 옆에 들어갈 FString Name이 들어간다.

🚩 Draw 함수

CheckBox의 Draw 함수에서는 UniformGridPanel을 생성한 뒤, Panel의 넓이를 설정하고, InternalDatas의 개수만큼 Slot을 추가하였다.

UniformGridPanel은 모든 자식 간에 사용 가능한 공간을 균등하게 나누어 배치해주는 패널이다.

 

해당 슬록 안에는 모두 체크박스를 그려서 넣어주었고, 이렇게 만들어진 panel을 ToSharedRef 로 반환하여 SEquipmentData 클래스의 CustomizeHeader 함수에서 .ValueContent[ ] 내용안에 바로 추가될 수 있도록 하였다.

 

또한 Panel의 MinDesiredSlotWidth 항목을 150으로 설정하여 체크박스 간의 간격을 추가해주었다. 

🚩 Draw Check Box 함수

DrawCheckBox 함수에서는 체크박스 옆에 들어가는 Text 영역STextBlock으로 생성하고, InternalDatas의 Name 데이터로 Text를 초기화하였다.

 

Name 데이터는 Equipment Data의 구조체 정보인 Montage / Play Rate / Can Move / Use Control Rotation 이다.

☑️ Create CheckBoxes 객체 생성하기 

SWeaponDetailsView::CustomizeDetails

이렇게 만들어진 체크박스는 SWeaponDetailsView 클래스CustomizeDetails 함수에서 생성된다.

SEquipmentData의 CreateCheckBoxes 함수를 호출하여 SWeaponCheckBoxes 객체를 초기화하고, Equipment 카테고리의 속성으로  추가(AddPropertices)하였다.

🚩 Create Check Boxes 함수

CreateCheckBoxes 함수가 호출되면 MakeShareable을 통해 SWeaponCheckBoxes 객체가 생성 및  반환된다.

🚩 Add Properties 함수

AddProperties 함수에서는 매개변수로 받아오는 IPropertyHandle을 이용하여 하위 자식 객체의 개수를 불러오고, 해당 개수만큼 InternalDatas 배열에 InternalData를 추가한다.

 

이때 FInternalData의 생성자 매개변수로 IPropertyHandle의 자식 Handle을 전달해주면, 해당 Handle을 통해 FInternalData의 IPropertyHandle handle 변수와 FString Name 변수를 초기화하였다.

☑️ Equipment의 데이터가 기본 값인지 비교하기

체크박스를 추가한 뒤에는, FEquipmentData 구조체의 데이터가 기본값인지를 비교하여

기본값인 경우에는 접히는 카테고리 속성으로 나타나지 않고, 기본값에서 변경된 경우만 카테고리에 나타나도록 Check 함수들을 작성하였다.

 

각각 Value의 자료형에 대한 CheckDefault Object/Value 함수를 작성하였다.

InValue가 UObject* 자료형이라면 nullptr인지를 검사하고, float/bool/FVector 형이라면 InternalData의 값과 같은 값인지를 검사하였다. (UObject와 같이 객체가 포인터라면 CheckDefaultObject 함수를 호출하고, 아닌 경우는 CheckDefaultValue를 호출한다.

즉, 기본값인지를 검사하는 원리는 아주 간단하게 새로운 객체를 선언했을 당시의 값과 비교하는 것이다.

☑️ Customize Children에 CheckBox Properties 추가하기

SEquipmentData 클래스의 CustomizeHeader 함수로 헤더를 전부 구성했다면 CustomizeChildren 함수로 접혔다 펴지는 Property 들을 추가해주었다.

🚩 Draw Properties 함수

SWeaponCheckBoxes의 DrawProperties 함수에서는 InternalData 구조체의 bChecked 값을 비교하여 체크가 안된 상태(false)라면 그리지 않고 넘어갔다.

 

체크가 된 상황이라면 이전에 작성해봤던 테스트용 Child 추가 코드들과 거의 같은 구조로 자식 항목을 추가하였다.

 

IDetailChildrenBuilder 객체를 매개변수로 받아와 자식의 Equipment 데이터가 가진 name과 value를 가져오고, Content로 나타내주었다.

 

살짝 구조가 복잡한 듯 하지만 🚩호출 순서를 정리해보면 아래와 같다.


SWeaponDetailsView::MakeInstance 호출

SWeaponDetailsView::CustomizeDetails 호출

Attachment 카테고리, Equipment 카테고리 추가


SEquipmentData::CreateCheckBoxes 호출

SWeaponCheckBoxes::AddPropertices 호출

CWeaponStructures::FEquipment 구조체 개수만큼 반복문 수행
InternalData 추가

SEquipmentData::MakeInstance 호출

SEquipmentData::CustomizeHeader, SEquipmentData::CustomizeChildren 호출

SWeaponCheckBoxes->Draw 호출

SWeaponCheckBoxes::DrawProperties 호출


최종적으로 컴파일 후 프로그램을 실행시킨 결과는 아래와 같다.

 

모든 Equipment 값이 기본값으로만 이루어진 DA_Fist와 DA_Hammer는 Equipment Data 항목에 아무런 데이터가 나타나지 않는다.

 

반면, Montage와 Play Rate를 임의로 설정해둔 DA_Sword의 EquipmentData 데이터는 바뀐 내용들이 하위 속성으로 나타나는것을 확인할 수 있었다.

(체크박스 체크 안된것은 다음 시간에 처리하기로 하자...)


참조

https://docs.unrealengine.com/4.26/en-US/API/Runtime/UMG/Components/UUniformGridPanel/

 

UUniformGridPanel

A panel that evenly divides up available space between all of its children.

docs.unrealengine.com

728x90