PART 2 · 강의 1/3

Property Customization

IPropertyTypeCustomization으로 특정 타입의 프로퍼티가 디테일 패널에 표시되는 방식을 완전히 커스터마이징합니다

01

디테일 패널 커스터마이징 개요

두 가지 커스터마이징 인터페이스의 차이점

UE5는 디테일 패널 커스터마이징을 위한 두 가지 인터페이스를 제공합니다.

인터페이스 대상 적용 범위 등록 방식
IPropertyTypeCustomization 특정 구조체/타입 해당 타입이 나타나는 모든 곳 RegisterCustomPropertyTypeLayout
IDetailCustomization 특정 UClass 해당 클래스의 디테일 패널 전체 RegisterCustomClassLayout
언제 어떤 것을 사용하나?

IPropertyTypeCustomization: 커스텀 구조체(예: FMyVector, FColorRange)가 어디에서든 일관된 UI로 표시되어야 할 때 사용합니다. IDetailCustomization: 특정 액터/컴포넌트의 디테일 패널 레이아웃 자체를 재구성할 때 사용합니다.

02

IPropertyTypeCustomization 구현

커스텀 구조체의 에디터 표시를 완전히 재정의하기

대상 구조체 정의

MyTypes.h (Runtime 모듈) USTRUCT(BlueprintType) struct FHealthRange { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) float MinHealth = 0.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite) float MaxHealth = 100.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite) FLinearColor DisplayColor = FLinearColor::Green; };

커스터마이징 클래스

FHealthRangeCustomization.h (Editor 모듈) #pragma once #include "IPropertyTypeCustomization.h" class FHealthRangeCustomization : public IPropertyTypeCustomization { public: static TSharedRef<IPropertyTypeCustomization> MakeInstance(); // 헤더 행 커스터마이징 (접힌 상태에서 보이는 부분) virtual void CustomizeHeader( TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& Utils) override; // 자식 프로퍼티 커스터마이징 (펼친 상태에서 보이는 부분) virtual void CustomizeChildren( TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& Utils) override; private: TSharedPtr<IPropertyHandle> MinHealthHandle; TSharedPtr<IPropertyHandle> MaxHealthHandle; TSharedPtr<IPropertyHandle> ColorHandle; };
FHealthRangeCustomization.cpp #include "FHealthRangeCustomization.h" #include "DetailWidgetRow.h" #include "IDetailChildrenBuilder.h" TSharedRef<IPropertyTypeCustomization> FHealthRangeCustomization::MakeInstance() { return MakeShareable(new FHealthRangeCustomization); } void FHealthRangeCustomization::CustomizeHeader( TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& Utils) { // 자식 프로퍼티 핸들 가져오기 MinHealthHandle = PropertyHandle->GetChildHandle( GET_MEMBER_NAME_CHECKED(FHealthRange, MinHealth)); MaxHealthHandle = PropertyHandle->GetChildHandle( GET_MEMBER_NAME_CHECKED(FHealthRange, MaxHealth)); ColorHandle = PropertyHandle->GetChildHandle( GET_MEMBER_NAME_CHECKED(FHealthRange, DisplayColor)); // 헤더 행: Min~Max 형태로 한 줄에 표시 HeaderRow .NameContent() [ PropertyHandle->CreatePropertyNameWidget() ] .ValueContent() .MaxDesiredWidth(400.0f) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(2.0f) [ MinHealthHandle->CreatePropertyValueWidget() ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(4.0f, 0.0f) [ SNew(STextBlock) .Text(LOCTEXT("To", "~")) ] + SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(2.0f) [ MaxHealthHandle->CreatePropertyValueWidget() ] ]; } void FHealthRangeCustomization::CustomizeChildren( TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& Utils) { // 색상 프로퍼티만 자식으로 표시 ChildBuilder.AddProperty(ColorHandle.ToSharedRef()); }
03

등록 및 해제

에디터 모듈에서 프로퍼티 커스터마이징 등록하기

C++ - 모듈에서 등록 #include "PropertyEditorModule.h" void FMyGameEditorModule::StartupModule() { FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>( "PropertyEditor"); // 구조체 타입 이름으로 등록 PropertyModule.RegisterCustomPropertyTypeLayout( FHealthRange::StaticStruct()->GetFName(), FOnGetPropertyTypeCustomizationInstance::CreateStatic( &FHealthRangeCustomization::MakeInstance)); // 변경사항 반영 PropertyModule.NotifyCustomizationModuleChanged(); } void FMyGameEditorModule::ShutdownModule() { if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) { FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>( "PropertyEditor"); PropertyModule.UnregisterCustomPropertyTypeLayout( FHealthRange::StaticStruct()->GetFName()); } }
NotifyCustomizationModuleChanged

등록 후 반드시 NotifyCustomizationModuleChanged()를 호출해야 이미 열려 있는 디테일 패널에 변경이 반영됩니다.

04

IPropertyHandle 심화

프로퍼티 핸들을 통한 값 읽기/쓰기와 변경 감지

메서드 기능
GetValue(T&) 현재 값 읽기
SetValue(T) 값 설정 (Undo 지원)
GetChildHandle(Name) 자식 프로퍼티 접근
GetNumChildren() 자식 수 확인
SetOnPropertyValueChanged 값 변경 콜백 등록
CreatePropertyNameWidget() 이름 라벨 위젯 생성
CreatePropertyValueWidget() 기본 값 편집 위젯 생성
IsValidHandle() 핸들 유효성 검사
C++ - IPropertyHandle 활용 // 값 읽기 float MinVal; MinHealthHandle->GetValue(MinVal); // 값 쓰기 (트랜잭션 자동 처리) MaxHealthHandle->SetValue(200.0f); // 변경 감지 MinHealthHandle->SetOnPropertyValueChanged( FSimpleDelegate::CreateLambda([this]() { // Min 값이 변경될 때 호출 float Min, Max; MinHealthHandle->GetValue(Min); MaxHealthHandle->GetValue(Max); // Min이 Max보다 크면 Max를 조정 if (Min > Max) { MaxHealthHandle->SetValue(Min); } })); // 멀티 셀렉션에서 값이 다를 때 float Val; FPropertyAccess::Result Result = MinHealthHandle->GetValue(Val); if (Result == FPropertyAccess::MultipleValues) { // 여러 객체가 선택되어 값이 다른 경우 }
Undo/Redo 자동 지원

IPropertyHandle::SetValue()는 자동으로 Undo/Redo 트랜잭션을 생성합니다. 여러 프로퍼티를 한 번에 변경할 때는 GEditor->BeginTransaction() / EndTransaction()으로 그룹화하세요.

SUMMARY

핵심 요약

  • IPropertyTypeCustomization은 특정 구조체 타입의 디테일 패널 표시를 커스터마이징하며, 해당 타입이 사용되는 모든 곳에 적용됩니다
  • CustomizeHeader로 헤더 행을, CustomizeChildren으로 펼친 자식 프로퍼티를 커스터마이징합니다
  • IPropertyHandle로 프로퍼티 값을 읽고 쓰며, 변경 감지 콜백으로 상호 의존적인 프로퍼티 간 연동이 가능합니다
  • RegisterCustomPropertyTypeLayout으로 모듈 시작 시 등록하고, NotifyCustomizationModuleChanged로 변경을 반영합니다
  • IPropertyHandle::SetValue()Undo/Redo를 자동 지원하므로 별도의 트랜잭션 관리가 불필요합니다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: IPropertyTypeCustomization 구현

커스텀 USTRUCT(예: FMyRange - Min, Max 포함)에 대한 IPropertyTypeCustomization을 구현하세요. CustomizeHeader에서 Min~Max를 한 줄에 표시하는 커스텀 위젯을 만들고, PropertyEditorModule에 등록하세요.

실습 2: 드롭다운 프로퍼티 커스터마이징

FString 프로퍼티를 일반 텍스트 입력 대신 SComboBox 드롭다운으로 표시하는 커스터마이징을 구현하세요. 드롭다운 항목은 데이터 에셋이나 Enum에서 동적으로 가져오도록 구성하세요.

심화 과제: 에셋 프리뷰가 포함된 프로퍼티 위젯

TSoftObjectPtr 프로퍼티에 대해 텍스처 프리뷰 썸네일을 인라인으로 표시하는 커스텀 프로퍼티 위젯을 구현하세요. 에셋 피커 연동, 드래그앤드롭 지원, 클릭 시 에셋 에디터 열기 기능을 포함하세요.