UE5 のエディタ UI では、構造体のプロパティを折りたたむことができますが、その状態では中身が見えないという課題があります。そこで、プロパティ名の右側の空欄部分に内容を簡略化して表示できるよう、汎用的な PropertyCustomization
を作成しました。また、TStructOpsTypeTraits
で構造体を拡張することで、適切なテキスト出力をすることができます。今回はこの 2 つのエディタ拡張について紹介します。
構造体の追加
まずは、適当な Animation プロパティを持つ構造体を定義します。
USTRUCT()
struct FAnimationNode
{
GENERATED_BODY()
UPROPERTY( EditDefaultsOnly )
TSoftObjectPtr<class UAnimationAsset> Animation;
};
// ...どこかのPROPERTY
public:
UPROPERTY( EditDefaultsOnly )
struct FAnimationNode Node;
PropertyCustomization に登録する
IPropertyTypeCustomization
を継承したエディタ UI を拡張するクラスを作成し、StartupModule
から PropertyCustomization
を登録します。
// プロパティ列を拡張する UI クラス
class FNamedStructureDetails final : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
return MakeShared<FNamedStructureDetails>();
}
protected:
// ヘッダー行をカスタマイズ
virtual void CustomizeHeader(
TSharedRef<IPropertyHandle> PropertyHandle,
FDetailWidgetRow& HeaderRow,
IPropertyTypeCustomizationUtils& CustomizationUtils ) override
{
HeaderRow
.NameContent()
[
// プロパティ名を表示
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew( SHorizontalBox )
+ SHorizontalBox::Slot()
.VAlign( VAlign_Center ) // 表示位置を縦方向の中央に寄せる
[
SNew( STextBlock )
.Text_Lambda( [PropertyHandle]
{
// 構造体から文字列を取得
FText DisplayText;
PropertyHandle->GetValueAsDisplayText( DisplayText );
return DisplayText;
} )
]
];
}
// 子要素は通常のレイアウトと同様に表示
virtual void CustomizeChildren(
TSharedRef<IPropertyHandle> PropertyHandle,
IDetailChildrenBuilder& ChildBuilder,
IPropertyTypeCustomizationUtils& CustomizationUtils ) override
{
uint32 NumChildren;
PropertyHandle->GetNumChildren( NumChildren );
for ( uint32 Index = 0; Index < NumChildren; ++Index )
{
// 通常と同様に、構造体の子要素を追加
ChildBuilder.AddProperty( PropertyHandle->GetChildHandle( Index ).ToSharedRef() );
}
}
};
class FMyProjectModule final : public FDefaultGameModuleImpl
{
virtual void StartupModule() override
{
auto& PropertyModule =
FModuleManager::LoadModuleChecked<FPropertyEditorModule>( "PropertyEditor" );
// PropertyCustomizationに登録
PropertyModule.RegisterCustomPropertyTypeLayout(
"NamedStructure", // 後述のPresentAsTypeで指定する
FOnGetPropertyTypeCustomizationInstance::CreateStatic(
&FNamedStructureDetails::MakeInstance
)
);
}
};
IMPLEMENT_PRIMARY_GAME_MODULE( FMyProjectModule, MyProject, "MyProject" );
(本来はエディタでのみ使う機能なので、EditorModule に追加してください)
PresentAsType メタ指定子
構造体にメタ指定子を追加します。
// 登録したPropertyCustomization名を指定する
USTRUCT( meta = ( PresentAsType = "NamedStructure" ) )
struct FAnimationNode
通常、FPropertyEditorModule::RegisterCustomPropertyTypeLayout
ではプロパティ名を登録しますが、PresentAsType
を使用すると、どんな型でも PropertyCustomization
を適用することが可能です。
ヘッダー欄に構造体の内容が、テキストとして表示されるようになりました。
ExportTextItem でテキスト出力を実装
TStructOpsTypeTraits
に WithExportTextItem
特性を追加し、さらに構造体に ExportTextItem
関数を定義することで、構造体のテキスト出力フォーマットを拡張できます。今回は、PPF_PropertyWindow
の場合、つまりエディタ UI での表示を拡張する例を示します。
struct FAnimationNode
{
// UStruct Overrides
bool ExportTextItem(
FString& ValueStr, const FAnimationNode& DefaultValue,
UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const
{
if ( PortFlags & PPF_PropertyWindow )
{
// AnimationがセットされていればAssetNameを表示
ValueStr += !Animation.IsNull() ?
Animation.GetAssetName() : FName( NAME_None ).ToString();
return true;
}
return false;
}
// ...略
template <>
struct TStructOpsTypeTraits<FAnimationNode> : TStructOpsTypeTraitsBase2<FAnimationNode>
{
enum
{
WithExportTextItem = true,
};
};
ValueStr +=
としているのは、呼び出し元の実装を引き継ぐことを想定するためです。配列であれば呼び出し元で "()"
が付与されるなど。
以上で、テキスト出力を自由にカスタマイズできるようになりました。
配列の子要素でテキストを表示する
UPROPERTY( EditDefaultsOnly )
TArray<FAnimationNode> Nodes;
配列でも PropertyCustomization
が適用されてヘッダー欄にテキストが表示されます。TitleProperty
メタ指定子の必要もありません。
FInstancedStructでも!
FInstancedStruct
を入れ子にする場合は、FInstancedStruct
自身が ExportTextItem
を実装しており、内部の構造体のテキスト出力関数を呼び出してくれます。今回のケースでは FAnimationNode
を FAnimationNode_Simple
や FAnimationNode_Random
に拡張するアイデアがあったため、この手法を採用しました。
まとめ
今回のカスタマイズにより、UE5 のエディタ UI で構造体の内容を柔軟に表示できるようになりました。特に、構造体の中身をテキスト形式で簡略表示することで、UI の使いやすさが向上しました。エディタでのプロパティ表示をカスタマイズしたい方には、今回紹介した手法が役立つでしょう。